Compare commits
1361 Commits
print_view
...
shift-1551
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07e252d58f | ||
|
|
5143b87b3a | ||
|
|
b1ef176dbc | ||
|
|
4226642b1b | ||
|
|
e40ba0f79c | ||
|
|
b8c0279105 | ||
|
|
aae215a968 | ||
|
|
bb847b9d99 | ||
|
|
a1c85fef14 | ||
|
|
c65335a61e | ||
|
|
f95b014d2b | ||
|
|
91e4fddd83 | ||
|
|
2af991023d | ||
|
|
a993ab97c6 | ||
|
|
f002c65608 | ||
|
|
a7df6fb465 | ||
|
|
133e7598e0 | ||
|
|
c1a52ffa75 | ||
|
|
4f46313388 | ||
|
|
03b2cc9cd2 | ||
|
|
1a2bf8dc95 | ||
|
|
dd63fbeb84 | ||
|
|
59e435c418 | ||
|
|
f89f0a19b5 | ||
|
|
cbc6ef95cb | ||
|
|
0ceecc9e1d | ||
|
|
c816902025 | ||
|
|
cfb03cdca0 | ||
|
|
266f77b08c | ||
|
|
257d58c236 | ||
|
|
015f3d936c | ||
|
|
18d2a0ffd7 | ||
|
|
24afde0e46 | ||
|
|
8499faa55a | ||
|
|
c60dd809b8 | ||
|
|
297b8e33f2 | ||
|
|
b670b2014c | ||
|
|
440e969f52 | ||
|
|
14b79f2f1c | ||
|
|
00cf49a61f | ||
|
|
4f534e0e84 | ||
|
|
83a19fbbbf | ||
|
|
610cb884fc | ||
|
|
ba92cec62b | ||
|
|
d92e961a52 | ||
|
|
b13e74756a | ||
|
|
4ef3072766 | ||
|
|
e96e2461d3 | ||
|
|
7a2e2be169 | ||
|
|
8d2a5a7e4a | ||
|
|
b7b0e4fab5 | ||
|
|
a624a79b30 | ||
|
|
313135da6f | ||
|
|
58d27d1247 | ||
|
|
edfb28168f | ||
|
|
8d0e03bb06 | ||
|
|
855f6f77cf | ||
|
|
6236cffe14 | ||
|
|
322a71fbb8 | ||
|
|
4d9f8476f3 | ||
|
|
d7d93b14b2 | ||
|
|
d1af3ece6e | ||
|
|
8153b20984 | ||
|
|
a50f605c29 | ||
|
|
daf23edd10 | ||
|
|
2eaaeb8259 | ||
|
|
a02c62d62c | ||
|
|
e0232a8e84 | ||
|
|
6ea5693b2f | ||
|
|
030c2114d1 | ||
|
|
2cb18e3668 | ||
|
|
cd9f8be563 | ||
|
|
a02792e9bf | ||
|
|
41bb422244 | ||
|
|
54663d3342 | ||
|
|
2529f7369f | ||
|
|
909c33dccf | ||
|
|
1adc9f1aa9 | ||
|
|
49da9e58fd | ||
|
|
f3e288d078 | ||
|
|
988000952e | ||
|
|
6537f3794b | ||
|
|
d31718ba8a | ||
|
|
9dd4bc5fa8 | ||
|
|
df5f1bd522 | ||
|
|
ddffab9169 | ||
|
|
0c34073582 | ||
|
|
14674947cb | ||
|
|
51bccdbd66 | ||
|
|
f0fbb3cf36 | ||
|
|
0cc47aacbe | ||
|
|
fafd592290 | ||
|
|
40e754b8c3 | ||
|
|
483301db7a | ||
|
|
218606fbd6 | ||
|
|
c601b8e62c | ||
|
|
2bd68ec991 | ||
|
|
66842648ed | ||
|
|
ce54b9a7b5 | ||
|
|
8a5f6d2a5d | ||
|
|
1d86a5476f | ||
|
|
ca4d3f6bce | ||
|
|
2812f2ce92 | ||
|
|
5c623db798 | ||
|
|
edaf005fe1 | ||
|
|
4f6e407247 | ||
|
|
e30881239c | ||
|
|
bbde2cc4b2 | ||
|
|
16d18c79d7 | ||
|
|
a0d2cb8a03 | ||
|
|
1bb5dc7e69 | ||
|
|
58759acfe4 | ||
|
|
0cd5136052 | ||
|
|
b3c6fe5369 | ||
|
|
599718f84e | ||
|
|
d9a5452388 | ||
|
|
0fe49e04bf | ||
|
|
a98d3fb4dc | ||
|
|
8c670d1832 | ||
|
|
c232f490bc | ||
|
|
c7280953dd | ||
|
|
8f4c606c64 | ||
|
|
6740afab42 | ||
|
|
5df22b3e6a | ||
|
|
3d9d18a0d5 | ||
|
|
fc469707a3 | ||
|
|
77fdc370c7 | ||
|
|
301290fb6d | ||
|
|
07fffe2f79 | ||
|
|
0227a63fa5 | ||
|
|
27764b863c | ||
|
|
032fd75f9e | ||
|
|
0bf4f861f3 | ||
|
|
fd8f90cb52 | ||
|
|
b6c6b025c8 | ||
|
|
3d89e98d1f | ||
|
|
7c5110ed5d | ||
|
|
0a474f48ad | ||
|
|
c409bfd5be | ||
|
|
39d5d5b2e0 | ||
|
|
8a80d9009d | ||
|
|
f62b5df566 | ||
|
|
214757ab0b | ||
|
|
f130186b37 | ||
|
|
2244eebc3b | ||
|
|
4176792f2d | ||
|
|
1e6cef52c9 | ||
|
|
a0f4f30a50 | ||
|
|
4cbf6ac393 | ||
|
|
af7425d8e6 | ||
|
|
3fea909d3f | ||
|
|
7c37d40677 | ||
|
|
3a97c27350 | ||
|
|
e0516a52a8 | ||
|
|
a85ec6efb3 | ||
|
|
3795c74814 | ||
|
|
27954dc6d3 | ||
|
|
68c4187a09 | ||
|
|
b9834231f3 | ||
|
|
2be343ea1c | ||
|
|
109fe1b62c | ||
|
|
63d691a63c | ||
|
|
6f57d6b876 | ||
|
|
e0bad99ea1 | ||
|
|
e39eb09cfb | ||
|
|
64d397c3f3 | ||
|
|
465ac1d1e1 | ||
|
|
18d6becebc | ||
|
|
943a4093ad | ||
|
|
b0917a5131 | ||
|
|
0972c4e340 | ||
|
|
3bbd0fdbcd | ||
|
|
43a237bf95 | ||
|
|
95f867b267 | ||
|
|
e96daf469a | ||
|
|
f2cdfe9e47 | ||
|
|
929b67e768 | ||
|
|
0573dc136a | ||
|
|
48588f6a9e | ||
|
|
8214b11da5 | ||
|
|
36090bf83e | ||
|
|
bffb2fe82f | ||
|
|
500cbf5d92 | ||
|
|
88579b9bf3 | ||
|
|
e8bb9bde99 | ||
|
|
0ee3cca4da | ||
|
|
f89ee6b7f2 | ||
|
|
aebfb52c85 | ||
|
|
667bd7af0e | ||
|
|
bbf69bc582 | ||
|
|
f2b7a3d002 | ||
|
|
3fd9e3ab56 | ||
|
|
8e11466a54 | ||
|
|
dade9797d5 | ||
|
|
97c1e65ffc | ||
|
|
b4e22f4a21 | ||
|
|
58b6feb3ca | ||
|
|
41c4920d45 | ||
|
|
d1ddd8de98 | ||
|
|
f41307eb4a | ||
|
|
59de77feb0 | ||
|
|
8ebbcf6e80 | ||
|
|
24c6e836dd | ||
|
|
8e38b3898e | ||
|
|
ce9a5e35c9 | ||
|
|
b092779697 | ||
|
|
ab30a96d16 | ||
|
|
dab0fb16ad | ||
|
|
5be398bc99 | ||
|
|
fe4172957f | ||
|
|
ff3a59d347 | ||
|
|
f9aedea26f | ||
|
|
5abd2c7151 | ||
|
|
bfcaf4f37b | ||
|
|
5f4e1835bc | ||
|
|
c1f1ae6b64 | ||
|
|
c4fcc6c24e | ||
|
|
dd73ad9941 | ||
|
|
ac21f7569f | ||
|
|
4ef0158da4 | ||
|
|
4db3b3ba0e | ||
|
|
dc43d85323 | ||
|
|
62651f381c | ||
|
|
3e9098907a | ||
|
|
e18df250f8 | ||
|
|
be5c5a51da | ||
|
|
a728fad675 | ||
|
|
185629b310 | ||
|
|
30ebea4f2d | ||
|
|
b135c1eac2 | ||
|
|
88fef73d6f | ||
|
|
556a9039e9 | ||
|
|
cdfe6c21c1 | ||
|
|
b094ebdd66 | ||
|
|
526a7ddea6 | ||
|
|
bb5ad31cba | ||
|
|
549da2efed | ||
|
|
e5e586dc43 | ||
|
|
8a682beb0e | ||
|
|
699e9f75c9 | ||
|
|
759e30977b | ||
|
|
6cfdb49cc3 | ||
|
|
1195121bf0 | ||
|
|
8bc067b18b | ||
|
|
76f59f7b85 | ||
|
|
55ebb4671f | ||
|
|
8a9cf07063 | ||
|
|
ca9ff8cf19 | ||
|
|
7217d9c427 | ||
|
|
9d712ad8f1 | ||
|
|
f3e49e7010 | ||
|
|
ba94f1b920 | ||
|
|
edcd46dd67 | ||
|
|
5cf6c89dde | ||
|
|
58676b1f83 | ||
|
|
8ff7c30e5a | ||
|
|
cd989768d4 | ||
|
|
6cbdefe3d9 | ||
|
|
c6ecc0d8e8 | ||
|
|
e0f5663bf4 | ||
|
|
aafc8996c1 | ||
|
|
128da40cbf | ||
|
|
ea0460e97e | ||
|
|
d8e7123576 | ||
|
|
6f45ec655f | ||
|
|
ec17c168ea | ||
|
|
119b097521 | ||
|
|
c731633a84 | ||
|
|
6a4d6ade39 | ||
|
|
b3b4697fc9 | ||
|
|
72c706d697 | ||
|
|
8a7af24bd4 | ||
|
|
dd01bd3e5f | ||
|
|
59cade9f82 | ||
|
|
6bb9b79832 | ||
|
|
f8fe7b5803 | ||
|
|
4d6279d61c | ||
|
|
5ef581f328 | ||
|
|
20a59c343e | ||
|
|
d6feb522b7 | ||
|
|
951aee8292 | ||
|
|
6e2d7912b5 | ||
|
|
b20925b550 | ||
|
|
c8b213c190 | ||
|
|
942de9dce5 | ||
|
|
483f684b04 | ||
|
|
26774b4193 | ||
|
|
a7a597d609 | ||
|
|
1c12b9278a | ||
|
|
de4764bd05 | ||
|
|
c1e7a78d23 | ||
|
|
8894bb91cc | ||
|
|
c955126f01 | ||
|
|
ce53b48d04 | ||
|
|
6015aeddee | ||
|
|
7b04e30964 | ||
|
|
6794f5e783 | ||
|
|
6e41ceff39 | ||
|
|
7ab47ff0de | ||
|
|
92d24d8702 | ||
|
|
bcbfd46682 | ||
|
|
bfd96a695f | ||
|
|
f27e8534dc | ||
|
|
040cd7ddbf | ||
|
|
8d6b21a076 | ||
|
|
2d36b25017 | ||
|
|
1f9e4306ae | ||
|
|
5242e0b36e | ||
|
|
e50505532e | ||
|
|
f05ef18d55 | ||
|
|
f6eccd7277 | ||
|
|
4d1258c64b | ||
|
|
103cbfd038 | ||
|
|
47069ad3f4 | ||
|
|
317f620992 | ||
|
|
8f43694582 | ||
|
|
df30076ffd | ||
|
|
f81750617e | ||
|
|
e4534c4319 | ||
|
|
d6c09aae6b | ||
|
|
d3d5230d0c | ||
|
|
6f3c5c44a5 | ||
|
|
67b32ca14d | ||
|
|
bffaf477ea | ||
|
|
cba45ece12 | ||
|
|
3290d7f401 | ||
|
|
ef3827376d | ||
|
|
4ae8a91051 | ||
|
|
ff4819ac68 | ||
|
|
58af133853 | ||
|
|
8199cd2118 | ||
|
|
9f02b80cf1 | ||
|
|
d3e4e81168 | ||
|
|
38195c0a8f | ||
|
|
5fa11e4278 | ||
|
|
c39b52fcb5 | ||
|
|
ec65fc1e65 | ||
|
|
32d8646d96 | ||
|
|
e8835fc2b1 | ||
|
|
054a06c5dc | ||
|
|
9c61d2eb22 | ||
|
|
d66b6cfee6 | ||
|
|
89c0427b2f | ||
|
|
3fec10d447 | ||
|
|
f8b4981bfe | ||
|
|
130669a2f9 | ||
|
|
c2c79ee231 | ||
|
|
86f10bd702 | ||
|
|
b496a06fc0 | ||
|
|
f865a6cb37 | ||
|
|
89186ea4f8 | ||
|
|
fb19985186 | ||
|
|
ebc6e1221a | ||
|
|
2b91dcb700 | ||
|
|
9d2e333fd6 | ||
|
|
013ad1069c | ||
|
|
ec059717f6 | ||
|
|
418566db3f | ||
|
|
7be9463be6 | ||
|
|
51712bc7d6 | ||
|
|
7b889d22d2 | ||
|
|
e8aad989ec | ||
|
|
4006d64d60 | ||
|
|
d792d99375 | ||
|
|
b11036a2e5 | ||
|
|
01de69a250 | ||
|
|
5e1c2e7feb | ||
|
|
b842aa11e5 | ||
|
|
ff01078b60 | ||
|
|
b1e92293fc | ||
|
|
443f69bd82 | ||
|
|
f4decbf52e | ||
|
|
bd2c311e4f | ||
|
|
2dcab6d0b3 | ||
|
|
c68a97198f | ||
|
|
2702c3da2b | ||
|
|
da06e9afd5 | ||
|
|
1623f13539 | ||
|
|
5910982a4f | ||
|
|
74630b36b0 | ||
|
|
ace4a5d614 | ||
|
|
0d41947f64 | ||
|
|
78de3b3591 | ||
|
|
0a4a6e7ba3 | ||
|
|
090399b336 | ||
|
|
47afb15970 | ||
|
|
b1ba3376aa | ||
|
|
8c1e19e77c | ||
|
|
0801d1473c | ||
|
|
6d98878c72 | ||
|
|
2d404fdadc | ||
|
|
b264fde165 | ||
|
|
970ff25e5e | ||
|
|
9dd3eee65c | ||
|
|
957faa6651 | ||
|
|
cc7dcc6e81 | ||
|
|
f666cba104 | ||
|
|
7f35498919 | ||
|
|
297205ff91 | ||
|
|
6f99381d13 | ||
|
|
45a42b00ad | ||
|
|
6586858284 | ||
|
|
19cce15e54 | ||
|
|
62b8e4c46f | ||
|
|
74b7d27408 | ||
|
|
a60ffc0702 | ||
|
|
9b8524ba27 | ||
|
|
32526d77b8 | ||
|
|
ba7db8f7b3 | ||
|
|
04712ad252 | ||
|
|
5711a9e148 | ||
|
|
a0f40c2dfb | ||
|
|
34636016eb | ||
|
|
95027e329c | ||
|
|
c9778a73c7 | ||
|
|
628d2a0a0a | ||
|
|
cc0ff1ec1f | ||
|
|
6543540509 | ||
|
|
cd53fc6318 | ||
|
|
4934b6c4da | ||
|
|
59db38524b | ||
|
|
3f5cfc3a4b | ||
|
|
3443f02c0a | ||
|
|
9a012ca01e | ||
|
|
509ef34cca | ||
|
|
49c289a094 | ||
|
|
10e5d88fb6 | ||
|
|
ae64fb3fdb | ||
|
|
cac2fde504 | ||
|
|
bb38a96fd1 | ||
|
|
79dbcb10c9 | ||
|
|
7c80fdea58 | ||
|
|
5500a42744 | ||
|
|
ae46264707 | ||
|
|
f2d8665e54 | ||
|
|
db7110d6b2 | ||
|
|
4e06b597fe | ||
|
|
fe006d05d3 | ||
|
|
358b70e280 | ||
|
|
a060dde625 | ||
|
|
3cfed72af4 | ||
|
|
4c59989236 | ||
|
|
6c1adff5c8 | ||
|
|
9293bdca06 | ||
|
|
beeccbfb44 | ||
|
|
0d3d2e2e78 | ||
|
|
2af7605451 | ||
|
|
976cc1c86f | ||
|
|
cbbf3aa6c8 | ||
|
|
4f8ff98d5b | ||
|
|
d4fe81c290 | ||
|
|
cbdf03aa66 | ||
|
|
d756670c56 | ||
|
|
9ef7b0e64a | ||
|
|
2d7c0f7e5f | ||
|
|
ec8ddc197f | ||
|
|
b48e56bd46 | ||
|
|
753ca93371 | ||
|
|
04f71e7f6a | ||
|
|
5d129dd420 | ||
|
|
1c37c630aa | ||
|
|
d329d6104e | ||
|
|
482723f3bc | ||
|
|
cbc025b1ff | ||
|
|
bf8ceceabe | ||
|
|
fba4bba132 | ||
|
|
0ded40c037 | ||
|
|
6f486a37ff | ||
|
|
1ef822997b | ||
|
|
ea66629e98 | ||
|
|
84c9979fe3 | ||
|
|
867a992183 | ||
|
|
c3ad7d649c | ||
|
|
fc250e228d | ||
|
|
37e81568ea | ||
|
|
048d910d5b | ||
|
|
b162aba445 | ||
|
|
974627849b | ||
|
|
e18e9f699e | ||
|
|
cc9209d2de | ||
|
|
dd3d264e63 | ||
|
|
246f1373a8 | ||
|
|
b993f4270e | ||
|
|
6c51ef11b1 | ||
|
|
ae98f6276e | ||
|
|
7424a5987b | ||
|
|
2d08749207 | ||
|
|
db50e98ae3 | ||
|
|
7ec0925c69 | ||
|
|
df1361aa43 | ||
|
|
6db04c86df | ||
|
|
bec80b443c | ||
|
|
333501fe55 | ||
|
|
063553d4f7 | ||
|
|
19b9e50281 | ||
|
|
2a68b4aeff | ||
|
|
5e25150521 | ||
|
|
cb183d3645 | ||
|
|
96bce301a0 | ||
|
|
360f5b7538 | ||
|
|
77234f6580 | ||
|
|
088e6af0b5 | ||
|
|
16fb1018a2 | ||
|
|
e2e54677ee | ||
|
|
ad6fe855a9 | ||
|
|
c50c97d149 | ||
|
|
8b98ae15f0 | ||
|
|
261f84d5f5 | ||
|
|
29989ac24e | ||
|
|
7a93e94fa6 | ||
|
|
e33f73fe9f | ||
|
|
6291389df5 | ||
|
|
ed817dc414 | ||
|
|
7494fa6bc9 | ||
|
|
62e50dbe52 | ||
|
|
30c090ba2d | ||
|
|
7ff82e6043 | ||
|
|
2950fb1041 | ||
|
|
61d3e2fb49 | ||
|
|
fb18c1a0be | ||
|
|
68c082e0dc | ||
|
|
ee3deb9c63 | ||
|
|
c1505de8d6 | ||
|
|
10be434c13 | ||
|
|
92e22eead5 | ||
|
|
31db86abd3 | ||
|
|
8e70ff135a | ||
|
|
5ec52f7471 | ||
|
|
8bc57f98a5 | ||
|
|
6f4cee6334 | ||
|
|
6beccc5e60 | ||
|
|
fed8e10644 | ||
|
|
3c0121c1d0 | ||
|
|
02021e3fb9 | ||
|
|
ea447365fa | ||
|
|
60989d6766 | ||
|
|
a2960dc653 | ||
|
|
702499dd79 | ||
|
|
482c427e34 | ||
|
|
67910490bd | ||
|
|
fdb5ab2293 | ||
|
|
092d9d1e42 | ||
|
|
e657f11531 | ||
|
|
9aac183318 | ||
|
|
c8c2867305 | ||
|
|
3e1f71026c | ||
|
|
dc562d8c20 | ||
|
|
84ec5aea26 | ||
|
|
444c13c6ea | ||
|
|
12d5e4f7d2 | ||
|
|
0fb1639915 | ||
|
|
d01f7cf317 | ||
|
|
03725c8e0c | ||
|
|
3942489d21 | ||
|
|
51479c8bbc | ||
|
|
ea3364ab68 | ||
|
|
cb608d7fd1 | ||
|
|
7129008428 | ||
|
|
3b832f507f | ||
|
|
b5849500f9 | ||
|
|
9eaabf95a0 | ||
|
|
724f38abc2 | ||
|
|
0dfc083a91 | ||
|
|
16432f503a | ||
|
|
7c9433be5d | ||
|
|
e4ce71ff14 | ||
|
|
45c6406ff4 | ||
|
|
550e2b6bb8 | ||
|
|
a7bb890729 | ||
|
|
3d8f8faf01 | ||
|
|
55cf5877c4 | ||
|
|
dec3c4aff3 | ||
|
|
79e00c1191 | ||
|
|
12dc33244d | ||
|
|
6e37f945ac | ||
|
|
d75120000a | ||
|
|
9e4aab7165 | ||
|
|
6bc3209333 | ||
|
|
054ff42547 | ||
|
|
11b47b308b | ||
|
|
367ab8ddd5 | ||
|
|
4f5d4a0984 | ||
|
|
40489c53d6 | ||
|
|
93b760d53b | ||
|
|
e86996bc7e | ||
|
|
14244f45b6 | ||
|
|
1b9d90a322 | ||
|
|
736f74d083 | ||
|
|
8194c6efdb | ||
|
|
aae2a17ad1 | ||
|
|
f44150668c | ||
|
|
6eed2deb09 | ||
|
|
878c6e7031 | ||
|
|
1f4a73fab6 | ||
|
|
7a315523fe | ||
|
|
6f082e662b | ||
|
|
018c981c5a | ||
|
|
0149773a03 | ||
|
|
5d46d90725 | ||
|
|
0544e05f32 | ||
|
|
80ff42a41f | ||
|
|
90b7df45b9 | ||
|
|
32858b973a | ||
|
|
40ba8d0de1 | ||
|
|
8ddbb4e64f | ||
|
|
cc40c48aac | ||
|
|
522ab9e0f5 | ||
|
|
97187aa7eb | ||
|
|
d93a5aa623 | ||
|
|
a5b88982bf | ||
|
|
df71bdcada | ||
|
|
51bab2dd26 | ||
|
|
ed8da6ad1b | ||
|
|
18d0a04efc | ||
|
|
bb68ed3ad9 | ||
|
|
402ca07aa2 | ||
|
|
28dc358df1 | ||
|
|
3cf1e9d55d | ||
|
|
82b001ab5f | ||
|
|
7b272226ce | ||
|
|
78d26fb7f6 | ||
|
|
930842e685 | ||
|
|
b938cb42d8 | ||
|
|
4c7b6d130f | ||
|
|
af57ca4983 | ||
|
|
40c31a1ad7 | ||
|
|
7ae4a4177f | ||
|
|
6efd323fbf | ||
|
|
ed9dbcc777 | ||
|
|
c2cf7de41b | ||
|
|
32bd14bd2d | ||
|
|
f9cbecdb17 | ||
|
|
7bb29a0277 | ||
|
|
d5f7579e9f | ||
|
|
13fd43c45c | ||
|
|
c08ce901cc | ||
|
|
94bd11d3c9 | ||
|
|
59c6e26b29 | ||
|
|
bf7cc404f8 | ||
|
|
12a2c71b90 | ||
|
|
6e2eeba0f6 | ||
|
|
99a739fae3 | ||
|
|
0185f61c11 | ||
|
|
783f0c113d | ||
|
|
75a839cc21 | ||
|
|
577b5586b4 | ||
|
|
1474a16148 | ||
|
|
9baa2000e1 | ||
|
|
d0624dbefe | ||
|
|
ecb6e8d9a9 | ||
|
|
bbb299faf2 | ||
|
|
ba3b55cab0 | ||
|
|
67acca7bc8 | ||
|
|
e4b33c3b56 | ||
|
|
ed43c73cec | ||
|
|
a6fa795b41 | ||
|
|
3a6bac2e63 | ||
|
|
8e01c05e42 | ||
|
|
fabf9281e9 | ||
|
|
6588d409b8 | ||
|
|
92a3421a4e | ||
|
|
a42147ff34 | ||
|
|
0df1bc6894 | ||
|
|
9317076c5e | ||
|
|
1b5525c51f | ||
|
|
6019c80c7b | ||
|
|
aaa6cb24d4 | ||
|
|
1ef5ad500a | ||
|
|
9468acedfa | ||
|
|
6a951b6357 | ||
|
|
95f7742259 | ||
|
|
9f795306e5 | ||
|
|
6feaff1e7b | ||
|
|
9a168354ae | ||
|
|
309d242c4d | ||
|
|
5c174f829e | ||
|
|
3c428f2d7b | ||
|
|
6833716576 | ||
|
|
bd374d031a | ||
|
|
ef26f48f60 | ||
|
|
b07f8525db | ||
|
|
9f062701fa | ||
|
|
2c452daddf | ||
|
|
53a82d3f4d | ||
|
|
b5b8816279 | ||
|
|
7bc4127e8c | ||
|
|
06158cc413 | ||
|
|
cb49e7c9a6 | ||
|
|
1822027a8f | ||
|
|
c8dabc25e3 | ||
|
|
f2b10eeee8 | ||
|
|
4b52e1471c | ||
|
|
f6bba03375 | ||
|
|
b3813a7121 | ||
|
|
eb2a1396ca | ||
|
|
0fae18c4ba | ||
|
|
25ac83e944 | ||
|
|
a82e65e190 | ||
|
|
187bb90de0 | ||
|
|
293648582a | ||
|
|
51a306993c | ||
|
|
ec1851fa84 | ||
|
|
bbe748dbd3 | ||
|
|
406e8c5874 | ||
|
|
a4f71a9f0a | ||
|
|
3748498523 | ||
|
|
49d11103f7 | ||
|
|
dce9060820 | ||
|
|
de6206ce78 | ||
|
|
a181ba308a | ||
|
|
5a9ac01cf8 | ||
|
|
a5cd306b1d | ||
|
|
57d3abab5f | ||
|
|
b6eb3185d5 | ||
|
|
286f78778c | ||
|
|
4b95790e2f | ||
|
|
97883971f7 | ||
|
|
b3c12d4ee6 | ||
|
|
bea426d8e6 | ||
|
|
be60370eae | ||
|
|
7cc216eb38 | ||
|
|
8c90efe745 | ||
|
|
4b9f4423f6 | ||
|
|
932b589bd9 | ||
|
|
ce18b91b03 | ||
|
|
5f883310b5 | ||
|
|
69cc46c2b8 | ||
|
|
dc6951f341 | ||
|
|
f7ed336f99 | ||
|
|
47f287c031 | ||
|
|
5739393f8d | ||
|
|
13956254ce | ||
|
|
d6b69c8cc2 | ||
|
|
7a79fd18f0 | ||
|
|
e3ffe79c4c | ||
|
|
043325b966 | ||
|
|
a2696b799f | ||
|
|
eb8ef37808 | ||
|
|
8416c6df05 | ||
|
|
3aa4814342 | ||
|
|
186f322bb5 | ||
|
|
28e2e7c924 | ||
|
|
97351028b5 | ||
|
|
0ef0863b59 | ||
|
|
2e80b4ace8 | ||
|
|
0d896c2ef6 | ||
|
|
dc9df04237 | ||
|
|
b469a64db3 | ||
|
|
73f19ff4e7 | ||
|
|
22b4fac3ee | ||
|
|
c97884c8b0 | ||
|
|
b972fb514a | ||
|
|
a3871bd1f2 | ||
|
|
2069f99b2b | ||
|
|
882d55fd09 | ||
|
|
68ef975726 | ||
|
|
0685ff3818 | ||
|
|
b28839d907 | ||
|
|
7f0a947de4 | ||
|
|
c8fc4afe65 | ||
|
|
3b7162cb02 | ||
|
|
4245456382 | ||
|
|
cdf43e31e2 | ||
|
|
f19d6b3c52 | ||
|
|
ca66e29072 | ||
|
|
6ff76a12f8 | ||
|
|
8b13997597 | ||
|
|
9600adee6b | ||
|
|
6eb3819492 | ||
|
|
95226f87bc | ||
|
|
ee3ae803b9 | ||
|
|
057667c425 | ||
|
|
02fa7daa1d | ||
|
|
2a2acf6d1c | ||
|
|
f644642fac | ||
|
|
3374a9e5a9 | ||
|
|
d3a74a5740 | ||
|
|
c458cc904a | ||
|
|
5c7b74e17e | ||
|
|
e07b0f65a1 | ||
|
|
b06fd5bbca | ||
|
|
6306f78fe0 | ||
|
|
da4bce0c89 | ||
|
|
053e815206 | ||
|
|
ab1053ecda | ||
|
|
9a61a3391b | ||
|
|
d3c19e28ec | ||
|
|
87115f2e50 | ||
|
|
5f7aadfba0 | ||
|
|
e954e066b4 | ||
|
|
e336182d79 | ||
|
|
3eb0743446 | ||
|
|
95c1c37ab1 | ||
|
|
a7054f0b1e | ||
|
|
e4bfabfabe | ||
|
|
6b56929a06 | ||
|
|
e3642bb513 | ||
|
|
ca7c416e19 | ||
|
|
ac6d964e28 | ||
|
|
e2772c816d | ||
|
|
916f7401f3 | ||
|
|
b53a71d523 | ||
|
|
510a2b0889 | ||
|
|
73057454c6 | ||
|
|
5a6cf2a296 | ||
|
|
9e3e04521e | ||
|
|
65dfbd02fe | ||
|
|
649ab53320 | ||
|
|
9250624f79 | ||
|
|
995e2090f5 | ||
|
|
9b91584776 | ||
|
|
8fd97ea501 | ||
|
|
a80b9ab362 | ||
|
|
556e1081b3 | ||
|
|
b070916f0b | ||
|
|
940caf14b0 | ||
|
|
76da1d6663 | ||
|
|
bafff9020a | ||
|
|
0d5dca6456 | ||
|
|
0f9b7119c0 | ||
|
|
d4181549e8 | ||
|
|
d57f56e44f | ||
|
|
f7777ca8a5 | ||
|
|
fce5530bc7 | ||
|
|
ad1530e9ff | ||
|
|
abb2dcbbe4 | ||
|
|
437499c5df | ||
|
|
f739c2c84a | ||
|
|
7d9b87f059 | ||
|
|
c157f4190e | ||
|
|
9357eca1cd | ||
|
|
40c65a07a4 | ||
|
|
13521bcf75 | ||
|
|
1c09dc139a | ||
|
|
d5f955b1e0 | ||
|
|
9e6e8f0931 | ||
|
|
c93ef30801 | ||
|
|
3e0dec4856 | ||
|
|
0b167f5f6f | ||
|
|
f6b21fdb82 | ||
|
|
f151628808 | ||
|
|
e44aad0328 | ||
|
|
1881054c92 | ||
|
|
f7533c5e41 | ||
|
|
f181e0fa55 | ||
|
|
b04efdfefc | ||
|
|
352b935dee | ||
|
|
0ba3b9975a | ||
|
|
cc06187f31 | ||
|
|
a916767392 | ||
|
|
1c57bfaa39 | ||
|
|
4a5adeb661 | ||
|
|
01f9772291 | ||
|
|
960b3aebed | ||
|
|
d75de73867 | ||
|
|
e75df97902 | ||
|
|
5be14ec750 | ||
|
|
717a82f46a | ||
|
|
e40038900b | ||
|
|
099eabc240 | ||
|
|
3a4fa35398 | ||
|
|
500d6a0cc2 | ||
|
|
38e5bf71bc | ||
|
|
248a05a916 | ||
|
|
2c141579dd | ||
|
|
45ff195f11 | ||
|
|
ce543c8179 | ||
|
|
5c11a8c1e0 | ||
|
|
f013a4c5ea | ||
|
|
194a22452e | ||
|
|
930a6a2ac8 | ||
|
|
444dea8b42 | ||
|
|
6cc3f69c2a | ||
|
|
36f3834ca5 | ||
|
|
d4b73b4fb9 | ||
|
|
b37f488117 | ||
|
|
ee0a9e834a | ||
|
|
c776e0e7e9 | ||
|
|
54d3193b6f | ||
|
|
326657c709 | ||
|
|
c72e86ea2e | ||
|
|
34b1ca29d3 | ||
|
|
1dc876a436 | ||
|
|
b28bc2c500 | ||
|
|
c38d98b00a | ||
|
|
7b83df088b | ||
|
|
c8b1240665 | ||
|
|
8a9d6bbdca | ||
|
|
6859b36e7c | ||
|
|
f0073c1528 | ||
|
|
f6b7e621b7 | ||
|
|
108a0179ca | ||
|
|
2baf65aa62 | ||
|
|
74c4e9665e | ||
|
|
ae9c22f327 | ||
|
|
37bca6febd | ||
|
|
30196793bd | ||
|
|
3060282ffb | ||
|
|
d63bba8db7 | ||
|
|
2c12ee01a0 | ||
|
|
aa76424a74 | ||
|
|
9188d6229e | ||
|
|
5555f32ffe | ||
|
|
59062980ff | ||
|
|
f4aac5f0b7 | ||
|
|
5c167aa2a9 | ||
|
|
8e1eed498e | ||
|
|
e449f39ea6 | ||
|
|
97171e0e1c | ||
|
|
5935ca4664 | ||
|
|
21c88cd311 | ||
|
|
5c786d8b70 | ||
|
|
d718d210ed | ||
|
|
f50c5d22b8 | ||
|
|
c36f9a432e | ||
|
|
65b6b02b1d | ||
|
|
9c65d7c057 | ||
|
|
eab07834cf | ||
|
|
eb38f33baf | ||
|
|
462f9f2f39 | ||
|
|
1021ccb230 | ||
|
|
84e9a3a7d6 | ||
|
|
c0060b3625 | ||
|
|
197aa12c61 | ||
|
|
dfb2959751 | ||
|
|
95fb4f0e45 | ||
|
|
b2c729b7b8 | ||
|
|
fe9b224a44 | ||
|
|
fc4e8c68f2 | ||
|
|
6fb1c03908 | ||
|
|
96ccfdb8cc | ||
|
|
75fd07e057 | ||
|
|
87fe69ecfb | ||
|
|
19b47030ca | ||
|
|
cf4e3fcc37 | ||
|
|
bef4133f51 | ||
|
|
7ac24efced | ||
|
|
d8d4a7075e | ||
|
|
dd4c9df6d1 | ||
|
|
da28c02b50 | ||
|
|
54858402e3 | ||
|
|
b39d8cc0b9 | ||
|
|
4c7c33800a | ||
|
|
d2c604a7ce | ||
|
|
91243fb6c0 | ||
|
|
940a85888a | ||
|
|
280c12e22b | ||
|
|
39e644d048 | ||
|
|
9acb3e5935 | ||
|
|
8ac5b5df61 | ||
|
|
434932599c | ||
|
|
81b8c445c6 | ||
|
|
002bb72a8d | ||
|
|
288770900e | ||
|
|
f6f6a23f8b | ||
|
|
522fa7be44 | ||
|
|
272d9e0552 | ||
|
|
9060a3cc13 | ||
|
|
b6a9c0e68b | ||
|
|
b43ae5be13 | ||
|
|
6384041107 | ||
|
|
89703cd9df | ||
|
|
f6aa9f1318 | ||
|
|
838e214b24 | ||
|
|
628c444cd4 | ||
|
|
e8289b0f45 | ||
|
|
86816f632f | ||
|
|
494710306b | ||
|
|
7a5fe4981f | ||
|
|
a67b320cae | ||
|
|
e3a2397b74 | ||
|
|
3b34654dd7 | ||
|
|
4b6437854c | ||
|
|
9ef20997a5 | ||
|
|
cfd10ae294 | ||
|
|
9b85e9a071 | ||
|
|
407962d998 | ||
|
|
1245289906 | ||
|
|
76c19202ed | ||
|
|
ada8195e2e | ||
|
|
83d6e9ad8a | ||
|
|
4469db0bd3 | ||
|
|
5e2dba5483 | ||
|
|
9de97694c3 | ||
|
|
8349065b0a | ||
|
|
bb82b2513e | ||
|
|
e781c170f3 | ||
|
|
43dfbd3d21 | ||
|
|
073c9f5f7c | ||
|
|
f9d67dd431 | ||
|
|
a2ea4c7fd0 | ||
|
|
a0358e32d7 | ||
|
|
c2023c5c56 | ||
|
|
43c310c82d | ||
|
|
939a0c44dc | ||
|
|
2b9cf1663b | ||
|
|
0a29e90701 | ||
|
|
d1be13e7d4 | ||
|
|
049a777ca8 | ||
|
|
0dcaa83a3e | ||
|
|
db706269e6 | ||
|
|
4f72505dc3 | ||
|
|
340f8b73a5 | ||
|
|
6c6b37000a | ||
|
|
b5c79624c6 | ||
|
|
49d66dedf4 | ||
|
|
ebbcdbc864 | ||
|
|
a18691c09f | ||
|
|
245b0b0f8f | ||
|
|
1d3b0478f9 | ||
|
|
a5d0307532 | ||
|
|
7daecdd53f | ||
|
|
667b4a49c3 | ||
|
|
2518e60a5e | ||
|
|
9ff8b62cee | ||
|
|
5086c80658 | ||
|
|
cb852fc20f | ||
|
|
fb3b34e0f6 | ||
|
|
8d4fc07f63 | ||
|
|
a0514ad8c1 | ||
|
|
4e03e525a4 | ||
|
|
0efdebcfd8 | ||
|
|
c7835d2d1d | ||
|
|
3e3bc0a347 | ||
|
|
184a22828f | ||
|
|
f26e27d23e | ||
|
|
e717f1e780 | ||
|
|
d1085a0f46 | ||
|
|
2e0913bb3b | ||
|
|
851ae46ea9 | ||
|
|
89a52b7551 | ||
|
|
15870d0e75 | ||
|
|
eb2c536221 | ||
|
|
bced7df539 | ||
|
|
fe672ed727 | ||
|
|
6e60594e66 | ||
|
|
f357dd690b | ||
|
|
255e0c3bdc | ||
|
|
8984d60c39 | ||
|
|
6dbfc8875b | ||
|
|
139f45872c | ||
|
|
eb223a4c09 | ||
|
|
c3531e9eba | ||
|
|
db3f8e5d68 | ||
|
|
8ca11542f8 | ||
|
|
36be23f7e4 | ||
|
|
51f67082f4 | ||
|
|
ea2f0cdd7b | ||
|
|
97d2e6f9d4 | ||
|
|
28ea75512c | ||
|
|
9b6683ae16 | ||
|
|
149d276e06 | ||
|
|
a519ebe19b | ||
|
|
66b537bc64 | ||
|
|
9722d29070 | ||
|
|
c748278637 | ||
|
|
23c39520e5 | ||
|
|
41f68d8a30 | ||
|
|
7320e823ad | ||
|
|
1aeda546fd | ||
|
|
91fcff5faf | ||
|
|
d97e54f85e | ||
|
|
4b58af8850 | ||
|
|
0822aa985d | ||
|
|
e5c6e294ec | ||
|
|
25fdde1807 | ||
|
|
fa45ca1453 | ||
|
|
67ec042ee3 | ||
|
|
8b6c88a7c6 | ||
|
|
9f8fddb4c5 | ||
|
|
8fcf7e3b9d | ||
|
|
d9326fc555 | ||
|
|
c1196599e1 | ||
|
|
a26279e0b9 | ||
|
|
29433882ea | ||
|
|
e3f511ec7c | ||
|
|
f0b18042f9 | ||
|
|
75c9936dbb | ||
|
|
ddd4065c81 | ||
|
|
ad88a72d0a | ||
|
|
6a00620552 | ||
|
|
a9db8d6898 | ||
|
|
65cc1bbd7e | ||
|
|
2935697209 | ||
|
|
3ae2454228 | ||
|
|
11b48ee636 | ||
|
|
2aa864afaa | ||
|
|
ec65e64a67 | ||
|
|
7e01d23aa2 | ||
|
|
f05b2ad9be | ||
|
|
eaaac76435 | ||
|
|
41160c64a8 | ||
|
|
0cfef59568 | ||
|
|
d953d1a889 | ||
|
|
4aa06f6a75 | ||
|
|
07a9bded95 | ||
|
|
f686e86afb | ||
|
|
9f28dae01b | ||
|
|
67ab584dc7 | ||
|
|
5da492cbf5 | ||
|
|
d871c529d1 | ||
|
|
7c2c5ea98d | ||
|
|
2e31a0530f | ||
|
|
5f66fb0bba | ||
|
|
143e9cdd61 | ||
|
|
aed32e6ada | ||
|
|
ed86c90b7e | ||
|
|
7dc606fd3b | ||
|
|
185fd559c9 | ||
|
|
473c684fa5 | ||
|
|
d3dbd82ce2 | ||
|
|
e5dc13e48c | ||
|
|
2a2d118973 | ||
|
|
f2f17402c9 | ||
|
|
39f764803d | ||
|
|
55176816aa | ||
|
|
1f7d4e0793 | ||
|
|
e17fae02ad | ||
|
|
3c1099a6a9 | ||
|
|
b1761ec246 | ||
|
|
03f0f13727 | ||
|
|
12648912aa | ||
|
|
4c898a8741 | ||
|
|
6bef8620e4 | ||
|
|
beb5560dce | ||
|
|
2ebe1ebc69 | ||
|
|
00092a079f | ||
|
|
9d313eb2d9 | ||
|
|
e8404c8720 | ||
|
|
e71e25955a | ||
|
|
fa9ac3c449 | ||
|
|
70854b2c42 | ||
|
|
e2a1be9762 | ||
|
|
f2c2fefd99 | ||
|
|
b5a960e933 | ||
|
|
66b2cc2e28 | ||
|
|
47246a3fdf | ||
|
|
4d01b2bb4f | ||
|
|
876715b3c5 | ||
|
|
93d74587e1 | ||
|
|
5bae74bc1b | ||
|
|
0259c91a06 | ||
|
|
04ccfc3002 | ||
|
|
495b7db72b | ||
|
|
6e5eb55b45 | ||
|
|
9ad99c1d81 | ||
|
|
622626bb27 | ||
|
|
f66575393a | ||
|
|
f67548cd70 | ||
|
|
cd63657a92 | ||
|
|
54f2b62294 | ||
|
|
1a1120220c | ||
|
|
82d93b6980 | ||
|
|
72b2b2d819 | ||
|
|
ad6352adc4 | ||
|
|
4170397094 | ||
|
|
1e15aca809 | ||
|
|
8234c3eb0f | ||
|
|
80eea7b064 | ||
|
|
779c28661e | ||
|
|
516f59f0fc | ||
|
|
bfb55da1a3 | ||
|
|
79e2e5c272 | ||
|
|
db1af98992 | ||
|
|
c8ea3ba79a | ||
|
|
96d5e072fe | ||
|
|
d25ba74123 | ||
|
|
f05dce8960 | ||
|
|
0758e73c5f | ||
|
|
617ee026c0 | ||
|
|
0cec64c056 | ||
|
|
915c730dae | ||
|
|
0451f1219a | ||
|
|
5290a95b6b | ||
|
|
e407695ff9 | ||
|
|
c5b53b00c1 | ||
|
|
d2ac9b9610 | ||
|
|
537e695ae9 | ||
|
|
b4b158da9e | ||
|
|
ad072c0546 | ||
|
|
713e1671d4 | ||
|
|
087f9756a2 | ||
|
|
eb0408703e | ||
|
|
7e961b690a | ||
|
|
81f3730d84 | ||
|
|
141fad8393 | ||
|
|
58ff63845e | ||
|
|
a8150b7864 | ||
|
|
ce6724f788 | ||
|
|
13faa8ab00 | ||
|
|
89beb73836 | ||
|
|
8b7e36a697 | ||
|
|
c0f3d89b0b | ||
|
|
ec62a59e57 | ||
|
|
f9269cfc63 | ||
|
|
56bc06746c | ||
|
|
497e94d8a0 | ||
|
|
aa77c8c528 | ||
|
|
6cd2a5d1a5 | ||
|
|
e4a3a1a35f | ||
|
|
06f3cc1345 | ||
|
|
3a6832ea58 | ||
|
|
d2bb7fc926 | ||
|
|
4b93f329c2 | ||
|
|
f03da92152 | ||
|
|
88acdbcc28 | ||
|
|
23623cca2c | ||
|
|
93e66aae54 | ||
|
|
02b831c174 | ||
|
|
cd20486fe2 | ||
|
|
1464f80425 | ||
|
|
8f673a7e3e | ||
|
|
c44d037933 | ||
|
|
c8b5b3f176 | ||
|
|
ce94470a10 | ||
|
|
e7592eeeb9 | ||
|
|
46253b421e | ||
|
|
7d4e77a7c8 | ||
|
|
9a4f21e0cb | ||
|
|
934aa3da7f | ||
|
|
e4244d60f1 | ||
|
|
0b6c6bf1df | ||
|
|
b35181c289 | ||
|
|
277564436b | ||
|
|
87a03ec1ed | ||
|
|
f8833241ef | ||
|
|
7f62c5cbb6 | ||
|
|
93b4749993 | ||
|
|
b2dac291da | ||
|
|
bec83d4343 | ||
|
|
4f3b3721c4 | ||
|
|
e5cf296b79 | ||
|
|
e1abdd1c7b | ||
|
|
71d8f1eb89 | ||
|
|
68c1568345 | ||
|
|
b5be0844ec | ||
|
|
f76e80ba68 | ||
|
|
ffbab554be | ||
|
|
0be50e803e | ||
|
|
7133a1b262 | ||
|
|
5876418eed | ||
|
|
950472b935 | ||
|
|
c0c5699e38 | ||
|
|
49fee3a211 | ||
|
|
a21ca92c90 | ||
|
|
260174dfd9 | ||
|
|
afc5e08716 | ||
|
|
89616727a1 | ||
|
|
ba55dfb841 | ||
|
|
65b956143c | ||
|
|
42e1987147 | ||
|
|
545e07455b | ||
|
|
b00594052c | ||
|
|
1c387795fe | ||
|
|
102f26cac1 | ||
|
|
c7e89ff879 | ||
|
|
565b8f5c7f | ||
|
|
3b314086f9 | ||
|
|
06fc140626 | ||
|
|
e4bfc6c5ae | ||
|
|
b82d835f4f | ||
|
|
d593365c9c | ||
|
|
ea6a903d8a | ||
|
|
9086e5dba7 | ||
|
|
cd10cd34f4 | ||
|
|
0d6a83197a | ||
|
|
6d784e36d7 | ||
|
|
9c88aa6974 | ||
|
|
1307146831 | ||
|
|
c385b4a082 | ||
|
|
1b961346f0 | ||
|
|
100db23210 | ||
|
|
e5d7bcb629 | ||
|
|
c2123e307a | ||
|
|
7a9d5bfc07 | ||
|
|
aed798800c | ||
|
|
dbfa952a69 | ||
|
|
aa58f08b3d | ||
|
|
3e980a4c57 | ||
|
|
d76871760c | ||
|
|
d29f5fa13e | ||
|
|
7275299165 | ||
|
|
9a3ac41370 | ||
|
|
9824f43780 | ||
|
|
1b7486c342 | ||
|
|
cd9ee8af90 | ||
|
|
cbf4fef45b | ||
|
|
8892a11e7e | ||
|
|
fc390dd107 | ||
|
|
99dfb51d70 | ||
|
|
04f8ebb4d8 | ||
|
|
41fb058adb | ||
|
|
f29146b319 | ||
|
|
ce0bd68716 | ||
|
|
f3f26b3824 | ||
|
|
7be3d6072f | ||
|
|
1d030b59df | ||
|
|
07d2d8c549 | ||
|
|
691ccbbebc | ||
|
|
9daa09277d | ||
|
|
79eaf62c9e | ||
|
|
dd078785ac | ||
|
|
12a8c54331 | ||
|
|
715fc2de59 | ||
|
|
af53559ca3 | ||
|
|
17b8ea9c86 | ||
|
|
1ad96e891b | ||
|
|
4e2b4195b4 | ||
|
|
b7492928ad | ||
|
|
9f04254963 | ||
|
|
7701c6097f | ||
|
|
275e1beda2 | ||
|
|
85b67dbb71 | ||
|
|
f659b7631d | ||
|
|
a602b2fd47 | ||
|
|
a56f6148fc | ||
|
|
f0bf77735d | ||
|
|
afd9282785 | ||
|
|
1344ed1d16 | ||
|
|
cbea096403 | ||
|
|
094edbd114 | ||
|
|
f016b6b988 | ||
|
|
782b35e0f1 | ||
|
|
70e9c6b947 | ||
|
|
4a457c96e8 | ||
|
|
2e2516825e | ||
|
|
392db81499 | ||
|
|
4ef161214d | ||
|
|
29d0380db3 | ||
|
|
d96498ab1f | ||
|
|
e6ccff103f | ||
|
|
e408b902f0 | ||
|
|
3d2d7684aa | ||
|
|
d5bc5caacd | ||
|
|
be6caf936e | ||
|
|
0b48fd1465 | ||
|
|
220537fbfb | ||
|
|
df5437647b | ||
|
|
92b2da9b1b | ||
|
|
ef56177372 | ||
|
|
cb7822576f | ||
|
|
7ba361b10d | ||
|
|
55694fa2fc | ||
|
|
c825878c46 | ||
|
|
80a69bfe90 | ||
|
|
d4dc8d2b79 | ||
|
|
4e3df93349 | ||
|
|
38efc62900 | ||
|
|
d1683d1c65 | ||
|
|
f038254038 | ||
|
|
a19582a5f3 | ||
|
|
00cbebd1e3 | ||
|
|
8c21d625fc | ||
|
|
fecee69de6 | ||
|
|
75366927f0 | ||
|
|
5ece721b00 | ||
|
|
0e2251c810 | ||
|
|
eccdcc373e | ||
|
|
5cdb52a249 | ||
|
|
ea38d6c2f3 | ||
|
|
a2ff8f9609 |
@@ -2590,10 +2590,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "QveenSi",
|
||||
"login": "qveensi",
|
||||
"name": "Yevhenii Huzii",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19945501?v=4",
|
||||
"profile": "https://github.com/QveenSi",
|
||||
"profile": "https://github.com/qveensi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
@@ -2607,15 +2607,6 @@
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "QveenSi",
|
||||
"name": "Yevhenii Huzii",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19945501?v=4",
|
||||
"profile": "https://github.com/QveenSi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chrisweirich",
|
||||
"name": "Christian Weirich",
|
||||
@@ -3316,6 +3307,888 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fvollmer",
|
||||
"name": "fvollmer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16699443?v=4",
|
||||
"profile": "https://github.com/fvollmer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "36864",
|
||||
"name": "36864",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/109086466?v=4",
|
||||
"profile": "https://github.com/36864",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CloCkWeRX",
|
||||
"name": "Daniel O'Connor",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/365751?v=4",
|
||||
"profile": "http://clockwerx.blogspot.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "BeatSpark",
|
||||
"name": "BeatSpark",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/102852568?v=4",
|
||||
"profile": "https://github.com/BeatSpark",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mrdahbi",
|
||||
"name": "mrdahbi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/59203607?v=4",
|
||||
"profile": "https://github.com/mrdahbi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chfsx",
|
||||
"name": "Fabian Schmid",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6661332?v=4",
|
||||
"profile": "http://sr.solutions",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "realchrisolin",
|
||||
"name": "Chris Olin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1288116?v=4",
|
||||
"profile": "https://www.chrisolin.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mnemonicly",
|
||||
"name": "Dan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3803132?v=4",
|
||||
"profile": "https://github.com/mnemonicly",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NebelKreis",
|
||||
"name": "Nebel",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43917728?v=4",
|
||||
"profile": "https://github.com/NebelKreis",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "test1337ahp",
|
||||
"name": "test1337ahp",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/132433803?v=4",
|
||||
"profile": "https://github.com/test1337ahp",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JonathonReinhart",
|
||||
"name": "Jonathon Reinhart",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1916566?v=4",
|
||||
"profile": "https://github.com/JonathonReinhart",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aranar-pro",
|
||||
"name": "aranar-pro",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/484742?v=4",
|
||||
"profile": "https://github.com/aranar-pro",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "phil-flip",
|
||||
"name": "Phil",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/27019397?v=4",
|
||||
"profile": "https://github.com/phil-flip",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fe80",
|
||||
"name": "Steffy Fort",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6473460?v=4",
|
||||
"profile": "https://fe80.fr/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sorvani",
|
||||
"name": "Jared Busch",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3302372?v=4",
|
||||
"profile": "https://github.com/sorvani",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "seanborg-codethink",
|
||||
"name": "seanborg-codethink",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/111956991?v=4",
|
||||
"profile": "https://github.com/seanborg-codethink",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dkaatz",
|
||||
"name": "dkaatz",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/160669961?v=4",
|
||||
"profile": "https://github.com/dkaatz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DanielRuf",
|
||||
"name": "Daniel Ruf",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/827205?v=4",
|
||||
"profile": "https://threema.id/74SF7MW6?text=",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ahpaleus",
|
||||
"name": "ahpaleus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38883201?v=4",
|
||||
"profile": "https://github.com/ahpaleus",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mink-adao-duy",
|
||||
"name": "Anh DAO-DUY",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22906055?v=4",
|
||||
"profile": "https://github.com/mink-adao-duy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Serdnad",
|
||||
"name": "Andres Gutierrez",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4723453?v=4",
|
||||
"profile": "https://github.com/Serdnad",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wewhite",
|
||||
"name": "Warren White",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/111083379?v=4",
|
||||
"profile": "https://github.com/wewhite",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "robintemme",
|
||||
"name": "Robin Temme",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2809241?v=4",
|
||||
"profile": "https://robintemme.de/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "herroworrd",
|
||||
"name": "herroworrd",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/47008367?v=4",
|
||||
"profile": "https://github.com/herroworrd",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vicleos",
|
||||
"name": "vicleos",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28558609?v=4",
|
||||
"profile": "https://mubiu.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "thinkl33t",
|
||||
"name": "Bob Clough",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1016780?v=4",
|
||||
"profile": "http://thinkl33t.co.uk/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "brandon-bailey",
|
||||
"name": "Brandon Daniel Bailey",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10648463?v=4",
|
||||
"profile": "https://github.com/brandon-bailey",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "marcquark",
|
||||
"name": "Marc Bartelt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23556080?v=4",
|
||||
"profile": "https://github.com/marcquark",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "manu-crealytics",
|
||||
"name": "manu-crealytics",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18286893?v=4",
|
||||
"profile": "https://github.com/manu-crealytics",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Galaxy102",
|
||||
"name": "Konstantin Köhring",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18245993?v=4",
|
||||
"profile": "https://www.galaxy102.de/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "deloz",
|
||||
"name": "Deloz",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/685167?v=4",
|
||||
"profile": "https://deloz.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mbrrg",
|
||||
"name": "Martin Berg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2682426?v=4",
|
||||
"profile": "https://github.com/mbrrg",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nothing4You",
|
||||
"name": "Richard Schwab",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3694534?v=4",
|
||||
"profile": "https://github.com/Nothing4You",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rickheil",
|
||||
"name": "Rick Heil",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8959676?v=4",
|
||||
"profile": "https://rickheil.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rosscdh",
|
||||
"name": "Ross Crawford-d'Heureuse",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/397106?v=4",
|
||||
"profile": "https://github.com/rosscdh",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "McG800",
|
||||
"name": "Ryan McGuire",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1621107?v=4",
|
||||
"profile": "https://github.com/McG800",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SBrown2021",
|
||||
"name": "SBrown2021",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/77835667?v=4",
|
||||
"profile": "https://github.com/SBrown2021",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "serkanerip",
|
||||
"name": "Serkan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8780913?v=4",
|
||||
"profile": "https://github.com/serkanerip",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Shankschn",
|
||||
"name": "Shanks",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/63188620?v=4",
|
||||
"profile": "https://www.yudelei.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cendai-mis",
|
||||
"name": "cendai-mis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/198525698?v=4",
|
||||
"profile": "https://github.com/cendai-mis",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "smcpeck",
|
||||
"name": "Shaun McPeck",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8724583?v=4",
|
||||
"profile": "https://smcpeck.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "snazy2000",
|
||||
"name": "Stephen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1378836?v=4",
|
||||
"profile": "https://github.com/snazy2000",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nevets82",
|
||||
"name": "Steven",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4462739?v=4",
|
||||
"profile": "http://nevets82.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Mateus-Romera",
|
||||
"name": "Mateus Villar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29017267?v=4",
|
||||
"profile": "https://mateusvillar.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mzack5020",
|
||||
"name": "Matthew Zackschewski",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12749393?v=4",
|
||||
"profile": "https://github.com/mzack5020",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "firefrei",
|
||||
"name": "Matthias Frei",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12660103?v=4",
|
||||
"profile": "https://www.frei.media/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nticaric",
|
||||
"name": "Nenad Ticaric",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/824840?v=4",
|
||||
"profile": "https://github.com/nticaric",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Scorcher",
|
||||
"name": "Nikolay Didenko",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/706439?v=4",
|
||||
"profile": "https://github.com/Scorcher",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nunomaduro",
|
||||
"name": "Nuno Maduro",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5457236?v=4",
|
||||
"profile": "https://nunomaduro.com/sponsorships",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "owalerys",
|
||||
"name": "Oliver Walerys",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8883074?v=4",
|
||||
"profile": "https://tektikhq.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rcmcdonald91",
|
||||
"name": "R. Christian McDonald",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3102039?v=4",
|
||||
"profile": "https://keybase.io/rcmcdonald91",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nixn",
|
||||
"name": "nix",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1525581?v=4",
|
||||
"profile": "https://nnix.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "octobunny",
|
||||
"name": "octobunny",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/55462380?v=4",
|
||||
"profile": "https://github.com/octobunny",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sreyemnayr",
|
||||
"name": "Ryan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8558670?v=4",
|
||||
"profile": "https://github.com/sreyemnayr",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "p3nj",
|
||||
"name": "p3nj",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1501022?v=4",
|
||||
"profile": "https://benji.ltd/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timwsuqld",
|
||||
"name": "Tim White",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6201617?v=4",
|
||||
"profile": "https://github.com/timwsuqld",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yannikp",
|
||||
"name": "yannikp",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22473767?v=4",
|
||||
"profile": "https://github.com/yannikp",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "viclou",
|
||||
"name": "victoria",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20525448?v=4",
|
||||
"profile": "https://github.com/viclou",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "valentyntu",
|
||||
"name": "Valentyn Tulub",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40685314?v=4",
|
||||
"profile": "https://github.com/valentyntu",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Wouter0100",
|
||||
"name": "Wouter van Os",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/864520?v=4",
|
||||
"profile": "http://wouter0100.nl/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xWyatt",
|
||||
"name": "Wyatt Teeter",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3946540?v=4",
|
||||
"profile": "https://www.linkedin.com/in/wyatt-teeter",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "terwey",
|
||||
"name": "Yorick Terweijden",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1596124?v=4",
|
||||
"profile": "https://github.com/terwey",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bmkalle",
|
||||
"name": "bmkalle",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/69298836?v=4",
|
||||
"profile": "https://github.com/bmkalle",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bricelabelle",
|
||||
"name": "bricelabelle",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28403467?v=4",
|
||||
"profile": "https://github.com/bricelabelle",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "corydlamb",
|
||||
"name": "corydlamb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/97770090?v=4",
|
||||
"profile": "https://github.com/corydlamb",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "splashx",
|
||||
"name": "Diogenes S. Jesus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1154133?v=4",
|
||||
"profile": "http://twitter.com/splash",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dkmansion",
|
||||
"name": "D M",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5826629?v=4",
|
||||
"profile": "https://github.com/dkmansion",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Jarli01",
|
||||
"name": "Dustin B",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14837699?v=4",
|
||||
"profile": "https://github.com/Jarli01",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fabiang",
|
||||
"name": "Fabian Grutschus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/348344?v=4",
|
||||
"profile": "https://github.com/fabiang",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MelonSmasher",
|
||||
"name": "MelonSmasher",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1491053?v=4",
|
||||
"profile": "https://github.com/MelonSmasher",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AlexanderWPapyrus",
|
||||
"name": "AlexanderWPapyrus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/80526133?v=4",
|
||||
"profile": "https://github.com/AlexanderWPapyrus",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "disc",
|
||||
"name": "Alexandr Hacicheant",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/306231?v=4",
|
||||
"profile": "https://github.com/disc",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hex128",
|
||||
"name": "Hex",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3032891?v=4",
|
||||
"profile": "https://hex128.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arukompas",
|
||||
"name": "Arunas Skirius",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8697942?v=4",
|
||||
"profile": "https://github.com/arukompas",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benperiton",
|
||||
"name": "Ben Periton",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/104396?v=4",
|
||||
"profile": "https://github.com/benperiton",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "byronwolfman",
|
||||
"name": "Byron Wolfman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11906832?v=4",
|
||||
"profile": "https://wolfman.dev/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "CalvinSchwartz",
|
||||
"name": "Calvin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/56485508?v=4",
|
||||
"profile": "https://github.com/CalvinSchwartz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "juanfont",
|
||||
"name": "Juan Font",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/181059?v=4",
|
||||
"profile": "https://github.com/juanfont",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "juhotaipale",
|
||||
"name": "Juho Taipale",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13137708?v=4",
|
||||
"profile": "https://github.com/juhotaipale",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "KorvinSzanto",
|
||||
"name": "Korvin Szanto",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1007419?v=4",
|
||||
"profile": "https://github.com/KorvinSzanto",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sniff122",
|
||||
"name": "Lewis Foster",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8513053?v=4",
|
||||
"profile": "https://lewisfoster.foo/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "loganswartz",
|
||||
"name": "Logan Swartzendruber",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33877541?v=4",
|
||||
"profile": "https://github.com/loganswartz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lopezio",
|
||||
"name": "Lorenzo P.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1156208?v=4",
|
||||
"profile": "https://github.com/lopezio",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "m4us1ne",
|
||||
"name": "Lukas Jung",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33946590?v=4",
|
||||
"profile": "https://github.com/m4us1ne",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LeafedFox",
|
||||
"name": "Ellie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10965027?v=4",
|
||||
"profile": "https://leafedfox.xyz/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gastamper",
|
||||
"name": "GA Stamper",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20960555?v=4",
|
||||
"profile": "https://github.com/gastamper",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gl-pup",
|
||||
"name": "Guillaume Lefranc",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/206553556?v=4",
|
||||
"profile": "https://github.com/gl-pup",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dasjoe",
|
||||
"name": "Hajo Möller",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/733892?v=4",
|
||||
"profile": "https://github.com/dasjoe",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pottom",
|
||||
"name": "Istvan Basa",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3420063?v=4",
|
||||
"profile": "https://github.com/pottom",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jjasghar",
|
||||
"name": "JJ Asghar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/810824?v=4",
|
||||
"profile": "https://jjasghar.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JemCdo",
|
||||
"name": "James E. Msenga",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40404495?v=4",
|
||||
"profile": "https://github.com/JemCdo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jfwiebe",
|
||||
"name": "Jan Felix Wiebe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6865786?v=4",
|
||||
"profile": "https://github.com/jfwiebe",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "drexljo",
|
||||
"name": "Jo Drexl",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43412008?v=4",
|
||||
"profile": "https://www.nfon.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "austinsasko",
|
||||
"name": "Austin Sasko",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4807843?v=4",
|
||||
"profile": "https://github.com/austinsasko",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JassonCordones",
|
||||
"name": "Jasson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4875039?v=4",
|
||||
"profile": "http://jassoncordones.github.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Tinyblargon",
|
||||
"name": "Okean",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/76069640?v=4",
|
||||
"profile": "https://github.com/Tinyblargon",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "amedranogil",
|
||||
"name": "Alejandro Medrano",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6515064?v=4",
|
||||
"profile": "https://www.lst.tfo.upm.es/alejandro-medrano/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lukaskraic",
|
||||
"name": "Lukas Kraic",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
|
||||
"profile": "https://github.com/lukaskraic",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -79,6 +79,13 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
|
||||
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
|
||||
BACKUP_ENV=true
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
|
||||
# --------------------------------------------
|
||||
#PHP_UPLOAD_LIMIT=10
|
||||
#PHP_POST_MAX_SIZE=10
|
||||
#PHP_UPLOAD_MAX_FILESIZE=10
|
||||
#PHP_MEMORY_LIMIT=10
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SESSION SETTINGS
|
||||
|
||||
11
.env.docker
11
.env.docker
@@ -28,6 +28,7 @@ PUBLIC_FILESYSTEM_DISK=local_public
|
||||
# --------------------------------------------
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=db
|
||||
DB_SOCKET=null
|
||||
DB_PORT='3306'
|
||||
DB_DATABASE=snipeit
|
||||
DB_USERNAME=snipeit
|
||||
@@ -84,6 +85,15 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
|
||||
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
|
||||
BACKUP_ENV=true
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
|
||||
# --------------------------------------------
|
||||
#PHP_UPLOAD_LIMIT=10
|
||||
#PHP_POST_MAX_SIZE=10
|
||||
#PHP_UPLOAD_MAX_FILESIZE=10
|
||||
#PHP_MEMORY_LIMIT=10
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SESSION SETTINGS
|
||||
# --------------------------------------------
|
||||
@@ -159,6 +169,7 @@ AWS_DEFAULT_REGION=null
|
||||
LOGIN_MAX_ATTEMPTS=5
|
||||
LOGIN_LOCKOUT_DURATION=60
|
||||
RESET_PASSWORD_LINK_EXPIRES=900
|
||||
INVITE_PASSWORD_LINK_EXPIRES=1500
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MISC
|
||||
|
||||
@@ -24,6 +24,7 @@ PUBLIC_FILESYSTEM_DISK=local_public
|
||||
# --------------------------------------------
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_SOCKET=null
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=null
|
||||
DB_USERNAME=null
|
||||
@@ -174,11 +175,13 @@ LOGIN_AUTOCOMPLETE=false
|
||||
RESET_PASSWORD_LINK_EXPIRES=15
|
||||
PASSWORD_CONFIRM_TIMEOUT=10800
|
||||
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
|
||||
INVITE_PASSWORD_LINK_EXPIRES=1500
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MISC
|
||||
# --------------------------------------------
|
||||
LOG_CHANNEL=single
|
||||
LOG_DEPRECATIONS=false
|
||||
LOG_MAX_DAYS=10
|
||||
APP_LOCKED=false
|
||||
APP_CIPHER=AES-256-CBC
|
||||
|
||||
10
.github/workflows/codacy-analysis.yml
vendored
10
.github/workflows/codacy-analysis.yml
vendored
@@ -10,10 +10,10 @@ name: Codacy Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
branches: [ develop ]
|
||||
schedule:
|
||||
- cron: '36 23 * * 3'
|
||||
|
||||
@@ -22,11 +22,11 @@ permissions:
|
||||
|
||||
jobs:
|
||||
codacy-security-scan:
|
||||
# Ensure schedule job never runs on forked repos. It's only executed for 'snipe/snipe-it'
|
||||
# Ensure schedule job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||
if: (github.repository == 'snipe/snipe-it') || ((github.repository != 'snipe/snipe-it') && (github.event_name != 'schedule'))
|
||||
if: (github.repository == 'grokability/snipe-it') || ((github.repository != 'grokability/snipe-it') && (github.event_name != 'schedule'))
|
||||
name: Codacy Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.5
|
||||
uses: codacy/codacy-analysis-cli-action@v4.4.7
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
|
||||
6
.github/workflows/docker-alpine.yml
vendored
6
.github/workflows/docker-alpine.yml
vendored
@@ -20,8 +20,8 @@ permissions:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
|
||||
if: github.repository == 'snipe/snipe-it'
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=tag,suffix=-alpine
|
||||
type=semver,pattern=v{{major}}-latest-alpine
|
||||
type=semver,pattern=v{{major}}-latest-alpine
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Snipe-IT Docker image build for hub.docker.com
|
||||
name: Docker images
|
||||
name: Docker images (Ubuntu)
|
||||
|
||||
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
|
||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||
@@ -20,8 +20,8 @@ permissions:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
|
||||
if: github.repository == 'snipe/snipe-it'
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
|
||||
if: github.repository == 'grokability/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=tag
|
||||
type=semver,pattern=v{{major}}-latest
|
||||
type=semver,pattern=v{{major}}-latest
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
240
.pa11yci.json
Normal file
240
.pa11yci.json
Normal file
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"standard": "WCAG2AA",
|
||||
"level": "error",
|
||||
"defaults": {
|
||||
"useIncognitoBrowserContext": false,
|
||||
"timeout": 500000,
|
||||
"wait": 5000,
|
||||
"ignore" : [
|
||||
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.Fail",
|
||||
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
|
||||
],
|
||||
|
||||
"viewport": {
|
||||
"width": 1280,
|
||||
"height": 1024
|
||||
}
|
||||
},
|
||||
"urls": [
|
||||
{
|
||||
"__NOTE" : "this should always be FIRST (if browser context is preserved)",
|
||||
"url": "https://snipe-it.test/login",
|
||||
"actions": [
|
||||
"navigate to https://snipe-it.test/login",
|
||||
"screen capture tests/pa11y/login.png",
|
||||
"set field input[name='username'] to admin",
|
||||
"set field input[name='password'] to password",
|
||||
"click element button[type=submit]",
|
||||
"wait for url to be https://snipe-it.test/",
|
||||
"screen capture tests/pa11y/dashboard.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/admin",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/admin",
|
||||
"screen capture tests/pa11y/admin-settings.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/admin/branding",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/admin/branding",
|
||||
"screen capture tests/pa11y/admin-branding.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/admin/general",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/admin/general",
|
||||
"screen capture tests/pa11y/admin-general.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/hardware/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/hardware/create",
|
||||
"screen capture tests/pa11y/asset-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/hardware",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/hardware",
|
||||
"screen capture tests/pa11y/asset-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/hardware/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/hardware/1",
|
||||
"screen capture tests/pa11y/asset-detail.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/account/view-assets",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/account/view-assets",
|
||||
"screen capture tests/pa11y/profile.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/licences",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/licenses",
|
||||
"screen capture tests/pa11y/license-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/licences/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/licenses/create",
|
||||
"screen capture tests/pa11y/license-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/licences/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/licenses/1",
|
||||
"screen capture tests/pa11y/license-view.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/consumables",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/consumables",
|
||||
"screen capture tests/pa11y/consumable-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/consumables/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/consumables/create",
|
||||
"screen capture tests/pa11y/consumable-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/consumables/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/consumables/1",
|
||||
"screen capture tests/pa11y/consumable-view.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/accessories",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/accessories",
|
||||
"screen capture tests/pa11y/accessory-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/accessories/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/accessories/create",
|
||||
"screen capture tests/pa11y/accessory-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/accessories/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/accessories/1",
|
||||
"screen capture tests/pa11y/accessory-view.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/locations",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/locations",
|
||||
"screen capture tests/pa11y/location-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/locations/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/locations/create",
|
||||
"screen capture tests/pa11y/location-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/locations/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/locations/1",
|
||||
"screen capture tests/pa11y/location-view.png"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url" : "https://snipe-it.test/models",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/models",
|
||||
"screen capture tests/pa11y/model-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/models/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/models/create",
|
||||
"screen capture tests/pa11y/model-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/models/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/models/1",
|
||||
"screen capture tests/pa11y/model-view.png"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url" : "https://snipe-it.test/companies",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/companies",
|
||||
"screen capture tests/pa11y/company-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/companies/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/companies/create",
|
||||
"screen capture tests/pa11y/company-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/companies/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/companies/1",
|
||||
"screen capture tests/pa11y/company-view.png"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url" : "https://snipe-it.test/departments",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/departments",
|
||||
"screen capture tests/pa11y/department-list.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/departments/create",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/departments/create",
|
||||
"screen capture tests/pa11y/department-create.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url" : "https://snipe-it.test/departments/1",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/departments/1",
|
||||
"screen capture tests/pa11y/department-view.png"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url" : "https://snipe-it.test/invalid-url",
|
||||
"actions" : [
|
||||
"navigate to https://snipe-it.test/invalid-url",
|
||||
"screen capture tests/pa11y/404.png"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -42,19 +42,32 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") | [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") | [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/qveensi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qveensi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") | [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") | [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") | [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") | [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") | [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/16699443?v=4" width="110px;"/><br /><sub>fvollmer</sub>](https://github.com/fvollmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fvollmer "Code") | [<img src="https://avatars.githubusercontent.com/u/109086466?v=4" width="110px;"/><br /><sub>36864</sub>](https://github.com/36864)<br />[💻](https://github.com/snipe/snipe-it/commits?author=36864 "Code") | [<img src="https://avatars.githubusercontent.com/u/365751?v=4" width="110px;"/><br /><sub>Daniel O'Connor</sub>](http://clockwerx.blogspot.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CloCkWeRX "Code") | [<img src="https://avatars.githubusercontent.com/u/102852568?v=4" width="110px;"/><br /><sub>BeatSpark</sub>](https://github.com/BeatSpark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BeatSpark "Code") | [<img src="https://avatars.githubusercontent.com/u/59203607?v=4" width="110px;"/><br /><sub>mrdahbi</sub>](https://github.com/mrdahbi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrdahbi "Code") | [<img src="https://avatars.githubusercontent.com/u/6661332?v=4" width="110px;"/><br /><sub>Fabian Schmid</sub>](http://sr.solutions)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chfsx "Code") | [<img src="https://avatars.githubusercontent.com/u/1288116?v=4" width="110px;"/><br /><sub>Chris Olin</sub>](https://www.chrisolin.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=realchrisolin "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/3803132?v=4" width="110px;"/><br /><sub>Dan</sub>](https://github.com/mnemonicly)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mnemonicly "Code") | [<img src="https://avatars.githubusercontent.com/u/43917728?v=4" width="110px;"/><br /><sub>Nebel</sub>](https://github.com/NebelKreis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NebelKreis "Code") | [<img src="https://avatars.githubusercontent.com/u/132433803?v=4" width="110px;"/><br /><sub>test1337ahp</sub>](https://github.com/test1337ahp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=test1337ahp "Code") | [<img src="https://avatars.githubusercontent.com/u/1916566?v=4" width="110px;"/><br /><sub>Jonathon Reinhart</sub>](https://github.com/JonathonReinhart)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JonathonReinhart "Code") | [<img src="https://avatars.githubusercontent.com/u/484742?v=4" width="110px;"/><br /><sub>aranar-pro</sub>](https://github.com/aranar-pro)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aranar-pro "Code") | [<img src="https://avatars.githubusercontent.com/u/27019397?v=4" width="110px;"/><br /><sub>Phil</sub>](https://github.com/phil-flip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phil-flip "Code") | [<img src="https://avatars.githubusercontent.com/u/6473460?v=4" width="110px;"/><br /><sub>Steffy Fort</sub>](https://fe80.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fe80 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/3302372?v=4" width="110px;"/><br /><sub>Jared Busch</sub>](https://github.com/sorvani)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sorvani "Code") | [<img src="https://avatars.githubusercontent.com/u/111956991?v=4" width="110px;"/><br /><sub>seanborg-codethink</sub>](https://github.com/seanborg-codethink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanborg-codethink "Code") | [<img src="https://avatars.githubusercontent.com/u/160669961?v=4" width="110px;"/><br /><sub>dkaatz</sub>](https://github.com/dkaatz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkaatz "Code") | [<img src="https://avatars.githubusercontent.com/u/827205?v=4" width="110px;"/><br /><sub>Daniel Ruf</sub>](https://threema.id/74SF7MW6?text=)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielRuf "Code") | [<img src="https://avatars.githubusercontent.com/u/38883201?v=4" width="110px;"/><br /><sub>ahpaleus</sub>](https://github.com/ahpaleus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ahpaleus "Code") | [<img src="https://avatars.githubusercontent.com/u/22906055?v=4" width="110px;"/><br /><sub>Anh DAO-DUY</sub>](https://github.com/mink-adao-duy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mink-adao-duy "Code") | [<img src="https://avatars.githubusercontent.com/u/4723453?v=4" width="110px;"/><br /><sub>Andres Gutierrez</sub>](https://github.com/Serdnad)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Serdnad "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111083379?v=4" width="110px;"/><br /><sub>Warren White</sub>](https://github.com/wewhite)<br />[💻](https://github.com/snipe/snipe-it/commits?author=wewhite "Code") | [<img src="https://avatars.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://robintemme.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=robintemme "Code") | [<img src="https://avatars.githubusercontent.com/u/47008367?v=4" width="110px;"/><br /><sub>herroworrd</sub>](https://github.com/herroworrd)<br />[💻](https://github.com/snipe/snipe-it/commits?author=herroworrd "Code") | [<img src="https://avatars.githubusercontent.com/u/28558609?v=4" width="110px;"/><br /><sub>vicleos</sub>](https://mubiu.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vicleos "Code") | [<img src="https://avatars.githubusercontent.com/u/1016780?v=4" width="110px;"/><br /><sub>Bob Clough</sub>](http://thinkl33t.co.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thinkl33t "Code") | [<img src="https://avatars.githubusercontent.com/u/10648463?v=4" width="110px;"/><br /><sub>Brandon Daniel Bailey</sub>](https://github.com/brandon-bailey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brandon-bailey "Code") | [<img src="https://avatars.githubusercontent.com/u/23556080?v=4" width="110px;"/><br /><sub>Marc Bartelt</sub>](https://github.com/marcquark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcquark "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/18286893?v=4" width="110px;"/><br /><sub>manu-crealytics</sub>](https://github.com/manu-crealytics)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manu-crealytics "Code") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") | [<img src="https://avatars.githubusercontent.com/u/685167?v=4" width="110px;"/><br /><sub>Deloz</sub>](https://deloz.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deloz "Code") | [<img src="https://avatars.githubusercontent.com/u/2682426?v=4" width="110px;"/><br /><sub>Martin Berg</sub>](https://github.com/mbrrg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mbrrg "Code") | [<img src="https://avatars.githubusercontent.com/u/3694534?v=4" width="110px;"/><br /><sub>Richard Schwab</sub>](https://github.com/Nothing4You)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nothing4You "Code") | [<img src="https://avatars.githubusercontent.com/u/8959676?v=4" width="110px;"/><br /><sub>Rick Heil</sub>](https://rickheil.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rickheil "Code") | [<img src="https://avatars.githubusercontent.com/u/397106?v=4" width="110px;"/><br /><sub>Ross Crawford-d'Heureuse</sub>](https://github.com/rosscdh)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rosscdh "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1621107?v=4" width="110px;"/><br /><sub>Ryan McGuire</sub>](https://github.com/McG800)<br />[💻](https://github.com/snipe/snipe-it/commits?author=McG800 "Code") | [<img src="https://avatars.githubusercontent.com/u/77835667?v=4" width="110px;"/><br /><sub>SBrown2021</sub>](https://github.com/SBrown2021)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SBrown2021 "Code") | [<img src="https://avatars.githubusercontent.com/u/8780913?v=4" width="110px;"/><br /><sub>Serkan</sub>](https://github.com/serkanerip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=serkanerip "Code") | [<img src="https://avatars.githubusercontent.com/u/63188620?v=4" width="110px;"/><br /><sub>Shanks</sub>](https://www.yudelei.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Shankschn "Code") | [<img src="https://avatars.githubusercontent.com/u/198525698?v=4" width="110px;"/><br /><sub>cendai-mis</sub>](https://github.com/cendai-mis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cendai-mis "Code") | [<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="110px;"/><br /><sub>Shaun McPeck</sub>](https://smcpeck.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smcpeck "Code") | [<img src="https://avatars.githubusercontent.com/u/1378836?v=4" width="110px;"/><br /><sub>Stephen</sub>](https://github.com/snazy2000)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snazy2000 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/4462739?v=4" width="110px;"/><br /><sub>Steven</sub>](http://nevets82.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nevets82 "Code") | [<img src="https://avatars.githubusercontent.com/u/29017267?v=4" width="110px;"/><br /><sub>Mateus Villar</sub>](https://mateusvillar.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mateus-Romera "Code") | [<img src="https://avatars.githubusercontent.com/u/12749393?v=4" width="110px;"/><br /><sub>Matthew Zackschewski</sub>](https://github.com/mzack5020)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mzack5020 "Code") | [<img src="https://avatars.githubusercontent.com/u/12660103?v=4" width="110px;"/><br /><sub>Matthias Frei</sub>](https://www.frei.media/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=firefrei "Code") | [<img src="https://avatars.githubusercontent.com/u/824840?v=4" width="110px;"/><br /><sub>Nenad Ticaric</sub>](https://github.com/nticaric)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nticaric "Code") | [<img src="https://avatars.githubusercontent.com/u/706439?v=4" width="110px;"/><br /><sub>Nikolay Didenko</sub>](https://github.com/Scorcher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scorcher "Code") | [<img src="https://avatars.githubusercontent.com/u/5457236?v=4" width="110px;"/><br /><sub>Nuno Maduro</sub>](https://nunomaduro.com/sponsorships)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nunomaduro "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/8883074?v=4" width="110px;"/><br /><sub>Oliver Walerys</sub>](https://tektikhq.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=owalerys "Code") | [<img src="https://avatars.githubusercontent.com/u/3102039?v=4" width="110px;"/><br /><sub>R. Christian McDonald</sub>](https://keybase.io/rcmcdonald91)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rcmcdonald91 "Code") | [<img src="https://avatars.githubusercontent.com/u/1525581?v=4" width="110px;"/><br /><sub>nix</sub>](https://nnix.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nixn "Code") | [<img src="https://avatars.githubusercontent.com/u/55462380?v=4" width="110px;"/><br /><sub>octobunny</sub>](https://github.com/octobunny)<br />[💻](https://github.com/snipe/snipe-it/commits?author=octobunny "Code") | [<img src="https://avatars.githubusercontent.com/u/8558670?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/sreyemnayr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sreyemnayr "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>p3nj</sub>](https://benji.ltd/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=p3nj "Code") | [<img src="https://avatars.githubusercontent.com/u/6201617?v=4" width="110px;"/><br /><sub>Tim White</sub>](https://github.com/timwsuqld)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timwsuqld "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>yannikp</sub>](https://github.com/yannikp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=yannikp "Code") | [<img src="https://avatars.githubusercontent.com/u/20525448?v=4" width="110px;"/><br /><sub>victoria</sub>](https://github.com/viclou)<br />[💻](https://github.com/snipe/snipe-it/commits?author=viclou "Code") | [<img src="https://avatars.githubusercontent.com/u/40685314?v=4" width="110px;"/><br /><sub>Valentyn Tulub</sub>](https://github.com/valentyntu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=valentyntu "Code") | [<img src="https://avatars.githubusercontent.com/u/864520?v=4" width="110px;"/><br /><sub>Wouter van Os</sub>](http://wouter0100.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Wouter0100 "Code") | [<img src="https://avatars.githubusercontent.com/u/3946540?v=4" width="110px;"/><br /><sub>Wyatt Teeter</sub>](https://www.linkedin.com/in/wyatt-teeter)<br />[💻](https://github.com/snipe/snipe-it/commits?author=xWyatt "Code") | [<img src="https://avatars.githubusercontent.com/u/1596124?v=4" width="110px;"/><br /><sub>Yorick Terweijden</sub>](https://github.com/terwey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=terwey "Code") | [<img src="https://avatars.githubusercontent.com/u/69298836?v=4" width="110px;"/><br /><sub>bmkalle</sub>](https://github.com/bmkalle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bmkalle "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/28403467?v=4" width="110px;"/><br /><sub>bricelabelle</sub>](https://github.com/bricelabelle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bricelabelle "Code") | [<img src="https://avatars.githubusercontent.com/u/97770090?v=4" width="110px;"/><br /><sub>corydlamb</sub>](https://github.com/corydlamb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=corydlamb "Code") | [<img src="https://avatars.githubusercontent.com/u/1154133?v=4" width="110px;"/><br /><sub>Diogenes S. Jesus</sub>](http://twitter.com/splash)<br />[💻](https://github.com/snipe/snipe-it/commits?author=splashx "Code") | [<img src="https://avatars.githubusercontent.com/u/5826629?v=4" width="110px;"/><br /><sub>D M</sub>](https://github.com/dkmansion)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkmansion "Code") | [<img src="https://avatars.githubusercontent.com/u/14837699?v=4" width="110px;"/><br /><sub>Dustin B</sub>](https://github.com/Jarli01)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jarli01 "Code") | [<img src="https://avatars.githubusercontent.com/u/348344?v=4" width="110px;"/><br /><sub>Fabian Grutschus</sub>](https://github.com/fabiang)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fabiang "Code") | [<img src="https://avatars.githubusercontent.com/u/1491053?v=4" width="110px;"/><br /><sub>MelonSmasher</sub>](https://github.com/MelonSmasher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MelonSmasher "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM ubuntu:24.04
|
||||
LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
|
||||
|
||||
# No need to add `apt-get clean` here, reference:
|
||||
# - https://github.com/snipe/snipe-it/pull/9201
|
||||
# - https://github.com/grokability/snipe-it/pull/9201
|
||||
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive; \
|
||||
@@ -110,7 +110,7 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Get dependencies
|
||||
USER docker
|
||||
RUN composer install --no-dev --working-dir=/var/www/html
|
||||
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html && rm -rf /var/www/html/vendor/*/*/.git
|
||||
USER root
|
||||
|
||||
############### APPLICATION INSTALL/INIT #################
|
||||
|
||||
@@ -70,7 +70,7 @@ COPY --from=composer /usr/bin/composer /usr/local/bin
|
||||
ARG COMPOSER_ALLOW_SUPERUSER=1
|
||||
RUN set -eux; \
|
||||
# Download and extract snipeit tarball
|
||||
curl -o snipeit.tar.gz -fL "https://github.com/snipe/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
|
||||
curl -o snipeit.tar.gz -fL "https://github.com/grokability/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
|
||||
tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \
|
||||
rm snipeit.tar.gz; \
|
||||
# Install composer php dependencies
|
||||
|
||||
18
README.md
18
README.md
@@ -1,6 +1,6 @@
|
||||

|
||||

|
||||
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://app.codacy.com/gh/grokability/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/grokability/snipe-it/actions/workflows/tests.yml)
|
||||
[](#contributing) [](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
@@ -9,7 +9,7 @@ This is a FOSS project for asset management in IT Operations. Knowing who has wh
|
||||
|
||||
It is built on [Laravel 11](http://laravel.com).
|
||||
|
||||
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
|
||||
Snipe-IT is actively developed and we [release quite frequently](https://github.com/grokability/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
|
||||
|
||||
> [!TIP]
|
||||
> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
|
||||
@@ -44,7 +44,7 @@ For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.i
|
||||
-----
|
||||
### Bug Reports & Feature Requests
|
||||
|
||||
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
|
||||
Feel free to check out the [GitHub Issues for this project](https://github.com/grokability/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
|
||||
@@ -133,9 +133,15 @@ The ERD is available [online here](https://drawsql.app/templates/snipe-it).
|
||||
|
||||
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
|
||||
|
||||
-----
|
||||
|
||||
### Star History
|
||||
|
||||
[](https://www.star-history.com/#grokability/snipe-it&Date)
|
||||
|
||||
------
|
||||
### Announcement List
|
||||
|
||||
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
|
||||
|
||||
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
|
||||
|
||||
We also usually make smaller announcements on our social accounts, our Discord, and our blog, so be sure to subscribe to those if you're looking for more granular announcements.
|
||||
|
||||
@@ -11,6 +11,7 @@ make it impossible to backport security fixes on older versions.
|
||||
|
||||
| Version | Supported |
|
||||
|---------| ------------------ |
|
||||
| 8.x | :white_check_mark: |
|
||||
| 7.x | :white_check_mark: |
|
||||
| 6.x | :x: |
|
||||
| 5.1.x | :x: |
|
||||
|
||||
2
Vagrantfile
vendored
2
Vagrantfile
vendored
@@ -1,7 +1,7 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
|
||||
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/grokability/snipe-it/master/snipeit.sh"
|
||||
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
2
app.json
2
app.json
@@ -6,7 +6,7 @@
|
||||
"it asset"
|
||||
],
|
||||
"website": "https://snipeitapp.com/",
|
||||
"repository": "https://github.com/snipe/snipe-it",
|
||||
"repository": "https://github.com/grokability/snipe-it",
|
||||
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
|
||||
"success_url": "/setup",
|
||||
"env": {
|
||||
|
||||
68
app/Console/Commands/CleanIncorrectCheckoutAcceptances.php
Normal file
68
app/Console/Commands/CleanIncorrectCheckoutAcceptances.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CleanIncorrectCheckoutAcceptances extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:clean-checkout-acceptances';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = "Delete checkout acceptances for checkouts to non-users";
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$deletions = 0;
|
||||
$skips = 0;
|
||||
|
||||
// This walks *every* checkoutacceptance. That's gnarly. But necessary
|
||||
$this->withProgressBar(CheckoutAcceptance::all(), function ($checkoutAcceptance) use (&$deletions, &$skips) {
|
||||
$item = $checkoutAcceptance->checkoutable;
|
||||
$checkout_to_id = $checkoutAcceptance->assigned_to_id;
|
||||
if(is_null($item)) {
|
||||
$this->info("'Checkoutable' Item is null, going to next record");
|
||||
return; //'false' allegedly breaks execution entirely, so 'true' maybe doesn't? hrm. just straight return maybe?
|
||||
}
|
||||
if(get_class($item) == LicenseSeat::class) {
|
||||
$item = $item->license;
|
||||
}
|
||||
foreach($item->assetlog()->where('action_type','checkout')->get() as $assetlog) {
|
||||
if ($assetlog->target_id == $checkout_to_id && $assetlog->target_type != User::class) {
|
||||
//We have a checkout-to an ID for a non-User, which matches to an ID in the checkout_acceptances table
|
||||
|
||||
//now, let's compare the _times_ - are they close?
|
||||
//I'm picking `created_at` over `action_date` because I'm more interested in when the actionlogs
|
||||
//were _created_, not when they were alleged to have happened - those created_at times need to be within 'X' seconds of
|
||||
//each other (currently 5)
|
||||
if ($assetlog->created_at->diffInSeconds($checkoutAcceptance->created_at, true) <= 5) { //we're allowing for five _ish_ seconds of slop
|
||||
$deletions++;
|
||||
$checkoutAcceptance->forceDelete(); // HARD delete this record; it should have never been
|
||||
return;
|
||||
} else {
|
||||
//$this->info("The two records are too far apart");
|
||||
}
|
||||
} else {
|
||||
//$this->info("No match! checkout to id: " . $checkout_to_id." target_id: ".$assetlog->target_id." target_type: ".$assetlog->target_type);
|
||||
}
|
||||
}
|
||||
$skips++;
|
||||
});
|
||||
$this->error("Final deletion count: $deletions, and skip count: $skips");
|
||||
}
|
||||
}
|
||||
53
app/Console/Commands/DisableSAML.php
Normal file
53
app/Console/Commands/DisableSAML.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DisableSAML extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:saml-disable';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This is a rescue command that can be used to turn off SAML settings in the event that you managed to lock yourself out using bad SAML settings.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->confirm("\n****************************************************\nThis will disable SAML support. You will not be able \nto login with an account that does not exist \nlocally in the Snipe-IT local database. \n****************************************************\n\nDo you wish to continue? [y|N]")) {
|
||||
$setting = Setting::getSettings();
|
||||
$setting->saml_enabled = 0;
|
||||
if ($setting->save()) {
|
||||
$this->info('SAML has been set to disabled.');
|
||||
} else {
|
||||
$this->info('Unable to disable SAML.');
|
||||
}
|
||||
} else {
|
||||
$this->info('Canceled. No actions taken.');
|
||||
}
|
||||
}
|
||||
}
|
||||
32
app/Console/Commands/FixUpAssignedTypeWithoutAssignedTo.php
Normal file
32
app/Console/Commands/FixUpAssignedTypeWithoutAssignedTo.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FixUpAssignedTypeWithoutAssignedTo extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:assigned-type-fixup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
|
||||
$this->info("Assets with an assigned_type but no assigned_to are fixed");
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class GeneratePersonalAccessToken extends Command
|
||||
@@ -43,9 +41,8 @@ class GeneratePersonalAccessToken extends Command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(TokenRepository $tokenRepository, ValidationFactory $validation)
|
||||
public function __construct(TokenRepository $tokenRepository)
|
||||
{
|
||||
$this->validation = $validation;
|
||||
$this->tokenRepository = $tokenRepository;
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -76,7 +73,7 @@ class GeneratePersonalAccessToken extends Command
|
||||
|
||||
} else {
|
||||
|
||||
$this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.');
|
||||
$this->warn('Your API Token has been created. Be sure to copy this token now, as it WILL NOT be accessible again.');
|
||||
|
||||
if ($token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first()) {
|
||||
$this->info('API Token ID: '.$token->id);
|
||||
|
||||
@@ -182,7 +182,7 @@ class LdapSync extends Command
|
||||
// Inject location information fields
|
||||
for ($i = 0; $i < $results['count']; $i++) {
|
||||
$results[$i]['ldap_location_override'] = false;
|
||||
$results[$i]['location_id'] = 0;
|
||||
$results[$i]['location_id'] = null;
|
||||
}
|
||||
|
||||
// Grab subsets based on location-specific DNs, and overwrite location for these users.
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use Schema;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -66,8 +66,8 @@ class PaveIt extends Command
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
|
||||
|
||||
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
|
||||
\Schema::table('assets', function ($table) use ($custom_field) {
|
||||
if (Schema::hasColumn('assets', $custom_field->db_column)) {
|
||||
Schema::table('assets', function ($table) use ($custom_field) {
|
||||
$table->dropColumn($custom_field->db_column);
|
||||
});
|
||||
}
|
||||
@@ -84,8 +84,8 @@ class PaveIt extends Command
|
||||
}
|
||||
|
||||
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
|
||||
\DB::statement('delete from oauth_clients WHERE id > 2');
|
||||
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
|
||||
DB::statement('delete from oauth_clients WHERE id > 2');
|
||||
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
|
||||
|
||||
}
|
||||
}
|
||||
@@ -249,6 +249,7 @@ class RestoreFromBackup extends Command
|
||||
'storage/private_uploads/consumables',
|
||||
'storage/private_uploads/eula-pdfs',
|
||||
'storage/private_uploads/imports',
|
||||
'storage/private_uploads/locations',
|
||||
'storage/private_uploads/licenses',
|
||||
'storage/private_uploads/signatures',
|
||||
'storage/private_uploads/users',
|
||||
@@ -289,6 +290,7 @@ class RestoreFromBackup extends Command
|
||||
|
||||
$interesting_files = [];
|
||||
$boring_files = [];
|
||||
$unsafe_files = [];
|
||||
|
||||
for ($i = 0; $i < $za->numFiles; $i++) {
|
||||
$stat_results = $za->statIndex($i);
|
||||
@@ -327,8 +329,9 @@ class RestoreFromBackup extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
|
||||
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico',];
|
||||
|
||||
$good_extensions = config('filesystems.allowed_upload_extensions_array');
|
||||
|
||||
foreach (array_merge($private_files, $public_files) as $file) {
|
||||
$has_wildcard = (strpos($file, '*') !== false);
|
||||
if ($has_wildcard) {
|
||||
@@ -338,7 +341,9 @@ class RestoreFromBackup extends Command
|
||||
if ($last_pos !== false) {
|
||||
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
||||
if (!in_array($extension, $good_extensions)) {
|
||||
$this->warn('Potentially unsafe file ' . $raw_path . ' is being skipped');
|
||||
// gathering potentially unsafe files here to return at exit
|
||||
$unsafe_files[] = $raw_path;
|
||||
Log::debug('Potentially unsafe file '.$raw_path.' is being skipped');
|
||||
$boring_files[] = $raw_path;
|
||||
continue 2;
|
||||
}
|
||||
@@ -372,6 +377,7 @@ class RestoreFromBackup extends Command
|
||||
if ($this->option('sanitize-guess-prefix')) {
|
||||
$prefix = SQLStreamer::guess_prefix($sql_contents);
|
||||
$this->line($prefix);
|
||||
|
||||
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
|
||||
}
|
||||
|
||||
@@ -505,6 +511,11 @@ class RestoreFromBackup extends Command
|
||||
} else {
|
||||
$this->info(count($interesting_files).' files were succesfully transferred');
|
||||
}
|
||||
if (count($unsafe_files) > 0) {
|
||||
foreach ($unsafe_files as $unsafe_file) {
|
||||
$this->warn('Potentially unsafe file '.$unsafe_file.' was skipped');
|
||||
}
|
||||
}
|
||||
foreach ($boring_files as $boring_file) {
|
||||
$this->warn($boring_file.' was skipped.');
|
||||
}
|
||||
|
||||
@@ -99,8 +99,11 @@ class SendAcceptanceReminder extends Command
|
||||
foreach ($no_email_list as $user) {
|
||||
$rows[] = [$user['id'], $user['name']];
|
||||
}
|
||||
$this->info("The following users do not have an email address:");
|
||||
$this->table($headers, $rows);
|
||||
|
||||
if (!empty($rows)) {
|
||||
$this->info("The following users do not have an email address:");
|
||||
$this->table($headers, $rows);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class TestLocationsFMCS extends Command
|
||||
public function handle()
|
||||
{
|
||||
$this->info('This script checks for company ID inconsistencies if Full Multiple Company Support with scoped locations will be used.');
|
||||
$this->info('This could take few moments if have a very large dataset.');
|
||||
$this->info('This could take a few moments if have a very large dataset.');
|
||||
$this->newLine();
|
||||
|
||||
// if parameter location_id is set, only test this location
|
||||
|
||||
@@ -19,7 +19,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
if(Setting::getSettings()->alerts_enabled === 1) {
|
||||
if(Setting::getSettings()?->alerts_enabled === 1) {
|
||||
$schedule->command('snipeit:inventory-alerts')->daily();
|
||||
$schedule->command('snipeit:expiring-alerts')->daily();
|
||||
$schedule->command('snipeit:expected-checkin')->daily();
|
||||
|
||||
@@ -28,7 +28,7 @@ class CheckoutableCheckedIn
|
||||
$this->checkedOutTo = $checkedOutTo;
|
||||
$this->checkedInBy = $checkedInBy;
|
||||
$this->note = $note;
|
||||
$this->action_date = $action_date ?? date('Y-m-d');
|
||||
$this->action_date = $action_date ?? date('Y-m-d H:i:s');
|
||||
$this->originalValues = $originalValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
use JsonException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@@ -107,26 +108,42 @@ class Handler extends ExceptionHandler
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
|
||||
// API throttle requests are handled in the RouteServiceProvider configureRateLimiting() method, so we don't need to handle them here
|
||||
switch ($e->getStatusCode()) {
|
||||
case '404':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
|
||||
case '429':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
|
||||
case '405':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
|
||||
default:
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This handles API validation exceptions that happen at the Form Request level, so they
|
||||
// never even get to the controller where we normally nicely format JSON responses
|
||||
if ($e instanceof ValidationException) {
|
||||
$response = $this->invalidJson($request, $e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// This is traaaaash but it handles models that are not found while using route model binding :(
|
||||
// The only alternative is to set that at *each* route, which is crazypants
|
||||
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
|
||||
$ids = method_exists($e, 'getIds') ? $e->getIds() : [];
|
||||
|
||||
// This gets the MVC model name from the exception and formats in a way that's less fugly
|
||||
if (in_array('bulkedit', $ids, true)) {
|
||||
$error_array = session()->get('bulk_asset_errors');
|
||||
return redirect()
|
||||
->route('hardware.bulkedit')
|
||||
->withErrors($error_array, 'bulk_asset_errors')
|
||||
->withInput();
|
||||
}
|
||||
|
||||
// This gets the MVC model name from the exception and formats in a way that's less fugly
|
||||
$model_name = strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel())))));
|
||||
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
|
||||
|
||||
@@ -143,6 +160,10 @@ class Handler extends ExceptionHandler
|
||||
$route = 'maintenances.index';
|
||||
} elseif ($route === 'licenseseats.index') {
|
||||
$route = 'licenses.index';
|
||||
} elseif ($route === 'customfields.index') {
|
||||
$route = 'fields.index';
|
||||
} elseif ($route === 'customfieldsets.index') {
|
||||
$route = 'fields.index';
|
||||
}
|
||||
|
||||
return redirect()
|
||||
@@ -201,6 +222,7 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Models\Setting;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Carbon\Carbon;
|
||||
@@ -722,8 +723,8 @@ class Helper
|
||||
// The check and message that the user is still using the deprecated version
|
||||
$deprecations = [
|
||||
'ms_teams_deprecated' => array(
|
||||
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'),
|
||||
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
|
||||
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows') && (Setting::getSettings()->webhook_selected === 'microsoft'),
|
||||
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Dec 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
|
||||
];
|
||||
|
||||
// if item of concern is being used and its being used with the deprecated values return the notification array.
|
||||
@@ -876,6 +877,48 @@ class Helper
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is a video, so we can show a preview
|
||||
*
|
||||
* @param File $file
|
||||
* @return string | Boolean
|
||||
* @author [B. Wetherington] [<bwetherington@grokability.com>]
|
||||
* @since [v8.1.18]
|
||||
*/
|
||||
public static function checkUploadIsVideo($file)
|
||||
{
|
||||
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||
$filetype = @finfo_file($finfo, $file);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (($filetype == 'video/mp4') || ($filetype == 'video/quicktime') || ($filetype == 'video/mpeg') || ($filetype == 'video/ogg') || ($filetype == 'video/webm') || ($filetype == 'video/x-msvide')) {
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is audio, so we can show a preview
|
||||
*
|
||||
* @param File $file
|
||||
* @return string | Boolean
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
*/
|
||||
public static function checkUploadIsAudio($file)
|
||||
{
|
||||
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||
$filetype = @finfo_file($finfo, $file);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (($filetype == 'audio/mpeg') || ($filetype == 'audio/ogg')) {
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through the permissions in the permissions config file and determines if
|
||||
* permissions are granted based on a $selected_arr array.
|
||||
@@ -896,6 +939,12 @@ class Helper
|
||||
public static function selectedPermissionsArray($permissions, $selected_arr = [])
|
||||
{
|
||||
$permissions_arr = [];
|
||||
if (is_array($permissions)) {
|
||||
$permissions = json_encode($permissions);
|
||||
}
|
||||
|
||||
// Set default to empty JSON if the value is null
|
||||
$permissions = json_decode($permissions ?? '{}', JSON_OBJECT_AS_ARRAY);
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
for ($x = 0; $x < count($permission); $x++) {
|
||||
@@ -906,13 +955,13 @@ class Helper
|
||||
if (is_array($selected_arr)) {
|
||||
|
||||
if (array_key_exists($permission_name, $selected_arr)) {
|
||||
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
|
||||
$permissions_arr[$permission_name] = (int) $selected_arr[$permission_name];
|
||||
} else {
|
||||
$permissions_arr[$permission_name] = '0';
|
||||
$permissions_arr[$permission_name] = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
$permissions_arr[$permission_name] = '0';
|
||||
$permissions_arr[$permission_name] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1164,6 +1213,15 @@ class Helper
|
||||
// Misc
|
||||
'pdf' => 'far fa-file-pdf',
|
||||
'lic' => 'far fa-save',
|
||||
|
||||
// video
|
||||
'mov' => 'fa-solid fa-video',
|
||||
'mp4' => 'fa-solid fa-video',
|
||||
|
||||
// audio
|
||||
'ogg' => 'fa-solid fa-file-audio',
|
||||
'mp3' => 'fa-solid fa-file-audio',
|
||||
'wav' => 'fa-solid fa-file-audio',
|
||||
];
|
||||
|
||||
if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
|
||||
@@ -1307,25 +1365,24 @@ class Helper
|
||||
switch ($item) {
|
||||
case 'asset':
|
||||
return 'fas fa-barcode';
|
||||
break;
|
||||
case 'accessory':
|
||||
return 'fas fa-keyboard';
|
||||
break;
|
||||
case 'component':
|
||||
return 'fas fa-hdd';
|
||||
break;
|
||||
case 'consumable':
|
||||
return 'fas fa-tint';
|
||||
break;
|
||||
case 'license':
|
||||
return 'far fa-save';
|
||||
break;
|
||||
case 'location':
|
||||
return 'fas fa-map-marker-alt';
|
||||
break;
|
||||
case 'user':
|
||||
return 'fas fa-user';
|
||||
break;
|
||||
case 'supplier':
|
||||
return 'fa-solid fa-store';
|
||||
case 'manufacturer':
|
||||
return 'fa-solid fa-building';
|
||||
case 'category':
|
||||
return 'fa-solid fa-table-columns';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1475,59 +1532,67 @@ class Helper
|
||||
}
|
||||
|
||||
|
||||
static public function getRedirectOption($request, $id, $table, $item_id = null)
|
||||
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
|
||||
{
|
||||
|
||||
$redirect_option = Session::get('redirect_option');
|
||||
$checkout_to_type = Session::get('checkout_to_type');
|
||||
$redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
|
||||
$checkout_to_type = Session::get('checkout_to_type') ?? null;
|
||||
$checkedInFrom = Session::get('checkedInFrom');
|
||||
$other_redirect = Session::get('other_redirect');
|
||||
$backUrl = Session::pull('back_url', route('home'));
|
||||
|
||||
// return to previous page
|
||||
if ($redirect_option === 'back') {
|
||||
if ($backUrl === route('home')) {
|
||||
return redirect()->to($backUrl)
|
||||
->with('warning', trans('general.page_error'));
|
||||
}
|
||||
|
||||
return redirect()->to($backUrl);
|
||||
}
|
||||
|
||||
// return to index
|
||||
if ($redirect_option == 'index') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return route('hardware.index');
|
||||
case "Users":
|
||||
return route('users.index');
|
||||
case "Licenses":
|
||||
return route('licenses.index');
|
||||
case "Accessories":
|
||||
return route('accessories.index');
|
||||
case "Components":
|
||||
return route('components.index');
|
||||
case "Consumables":
|
||||
return route('consumables.index');
|
||||
}
|
||||
return match ($table) {
|
||||
'Assets' => redirect()->route('hardware.index'),
|
||||
'Users' => redirect()->route('users.index'),
|
||||
'Licenses' => redirect()->route('licenses.index'),
|
||||
'Accessories' => redirect()->route('accessories.index'),
|
||||
'Components' => redirect()->route('components.index'),
|
||||
'Consumables' => redirect()->route('consumables.index'),
|
||||
};
|
||||
}
|
||||
|
||||
// return to thing being assigned
|
||||
if ($redirect_option == 'item') {
|
||||
switch ($table) {
|
||||
case "Assets":
|
||||
return route('hardware.show', $id ?? $item_id);
|
||||
case "Users":
|
||||
return route('users.show', $id ?? $item_id);
|
||||
case "Licenses":
|
||||
return route('licenses.show', $id ?? $item_id);
|
||||
case "Accessories":
|
||||
return route('accessories.show', $id ?? $item_id);
|
||||
case "Components":
|
||||
return route('components.show', $id ?? $item_id);
|
||||
case "Consumables":
|
||||
return route('consumables.show', $id ?? $item_id);
|
||||
}
|
||||
return match ($table) {
|
||||
'Assets' => redirect()->route('hardware.show', $id ?? $item_id),
|
||||
'Users' => redirect()->route('users.show', $id ?? $item_id),
|
||||
'Licenses' => redirect()->route('licenses.show', $id ?? $item_id),
|
||||
'Accessories' => redirect()->route('accessories.show', $id ?? $item_id),
|
||||
'Components' => redirect()->route('components.show', $id ?? $item_id),
|
||||
'Consumables' => redirect()->route('consumables.show', $id ?? $item_id),
|
||||
};
|
||||
}
|
||||
|
||||
// return to assignment target
|
||||
if ($redirect_option == 'target') {
|
||||
switch ($checkout_to_type) {
|
||||
case 'user':
|
||||
return route('users.show', $request->assigned_user);
|
||||
case 'location':
|
||||
return route('locations.show', $request->assigned_location);
|
||||
case 'asset':
|
||||
return route('hardware.show', $request->assigned_asset);
|
||||
}
|
||||
return match ($checkout_to_type) {
|
||||
'user' => redirect()->route('users.show', $request->assigned_user ?? $checkedInFrom),
|
||||
'location' => redirect()->route('locations.show', $request->assigned_location ?? $checkedInFrom),
|
||||
'asset' => redirect()->route('hardware.show', $request->assigned_asset ?? $checkedInFrom),
|
||||
};
|
||||
}
|
||||
|
||||
// return to somewhere else
|
||||
if ($redirect_option == 'other_redirect') {
|
||||
return match ($other_redirect) {
|
||||
'audit' => redirect()->route('assets.audit.due'),
|
||||
'model' => redirect()->route('models.show', $request->model_id),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
||||
}
|
||||
|
||||
@@ -1555,6 +1620,11 @@ class Helper
|
||||
$locations = Location::all();
|
||||
}
|
||||
|
||||
// Bail out early if there are no locations
|
||||
if ($locations->count() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach($locations as $location) {
|
||||
// in case of an update of a single location, use the newly requested company_id
|
||||
if ($new_company_id) {
|
||||
@@ -1593,14 +1663,17 @@ class Helper
|
||||
$items = collect([])->push($location->$keyword);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($items as $item) {
|
||||
|
||||
|
||||
if ($item && $item->company_id != $location_company) {
|
||||
|
||||
$mismatched[] = [
|
||||
class_basename(get_class($item)),
|
||||
$item->id,
|
||||
$item->name ?? $item->asset_tag ?? $item->serial ?? $item->username,
|
||||
str_replace('App\\Models\\', '', $item->assigned_type) ?? null,
|
||||
$item->assigned_type ? str_replace('App\\Models\\', '', $item->assigned_type) : null,
|
||||
$item->company_id ?? null,
|
||||
$item->company->name ?? null,
|
||||
// $item->defaultLoc->id ?? null,
|
||||
@@ -1612,6 +1685,15 @@ class Helper
|
||||
$location_company ?? null,
|
||||
];
|
||||
|
||||
$count++;
|
||||
|
||||
// Bail early if this is not being run via artisan
|
||||
if ((!$artisan) && ($count > 0)) {
|
||||
return $mismatched;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ class StorageHelper
|
||||
$disk = config('filesystems.default');
|
||||
}
|
||||
switch (config("filesystems.disks.$disk.driver")) {
|
||||
case 'local':
|
||||
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
|
||||
case 'local':
|
||||
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
|
||||
|
||||
case 's3':
|
||||
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
|
||||
case 's3':
|
||||
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
|
||||
|
||||
default:
|
||||
return Storage::disk($disk)->download($filename);
|
||||
default:
|
||||
return Storage::disk($disk)->download($filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,21 +33,29 @@ class StorageHelper
|
||||
* to determine that they are safe to display inline.
|
||||
*
|
||||
* @author <A. Gianotto> [<snipe@snipe.net]>
|
||||
* @since v7.0.14
|
||||
* @param $file_with_path
|
||||
* @since v7.0.14
|
||||
* @param $file_with_path
|
||||
* @return bool
|
||||
*/
|
||||
public static function allowSafeInline($file_with_path) {
|
||||
public static function allowSafeInline($file_with_path)
|
||||
{
|
||||
|
||||
$allowed_inline = [
|
||||
'pdf',
|
||||
'svg',
|
||||
'jpg',
|
||||
'gif',
|
||||
'svg',
|
||||
'avif',
|
||||
'webp',
|
||||
'gif',
|
||||
'gif',
|
||||
'jpg',
|
||||
'mov',
|
||||
'mp3',
|
||||
'mp4',
|
||||
'ogg',
|
||||
'pdf',
|
||||
'png',
|
||||
'svg',
|
||||
'svg',
|
||||
'wav',
|
||||
'webm',
|
||||
'webp',
|
||||
];
|
||||
|
||||
|
||||
@@ -59,10 +67,24 @@ class StorageHelper
|
||||
|
||||
}
|
||||
|
||||
public static function getFiletype($file_with_path)
|
||||
{
|
||||
|
||||
// The file exists and is allowed to be displayed inline
|
||||
if (Storage::exists($file_with_path)) {
|
||||
return pathinfo($file_with_path, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decide whether to show the file inline or download it.
|
||||
*/
|
||||
public static function showOrDownloadFile($file, $filename) {
|
||||
public static function showOrDownloadFile($file, $filename)
|
||||
{
|
||||
|
||||
$headers = [];
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ class AccessoriesController extends Controller
|
||||
// Was the accessory created?
|
||||
if ($accessory->save()) {
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
->with('success', trans('admin/accessories/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
|
||||
@@ -167,7 +168,8 @@ class AccessoriesController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($accessory->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
} else {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
@@ -184,15 +186,15 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function destroy($accessoryId) : RedirectResponse
|
||||
{
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
if (is_null($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
|
||||
if ($accessory->hasUsers() > 0) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()]));
|
||||
if ($accessory->checkouts_count > 0) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/general.delete_disabled'));
|
||||
}
|
||||
|
||||
if ($accessory->image) {
|
||||
@@ -220,7 +222,10 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function show(Accessory $accessory) : View | RedirectResponse
|
||||
{
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessory->id);
|
||||
$accessory->loadCount('checkouts as checkouts_count');
|
||||
|
||||
$accessory->load(['adminuser' => fn($query) => $query->withTrashed()]);
|
||||
|
||||
$this->authorize('view', $accessory);
|
||||
return view('accessories.view', compact('accessory'));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
@@ -30,9 +29,17 @@ class AccessoryCheckinController extends Controller
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
|
||||
//based on what the accessory is checked out to the target redirect option will be displayed accordingly.
|
||||
$target_option = match ($accessory_user->assigned_type) {
|
||||
'App\Models\Asset' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset')]),
|
||||
'App\Models\Location' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.location')]),
|
||||
default => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.user')]),
|
||||
};
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
return view('accessories/checkin', compact('accessory'))->with('backto', $backto);
|
||||
return view('accessories/checkin', compact('accessory', 'target_option'))->with('backto', $backto);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,8 +58,14 @@ class AccessoryCheckinController extends Controller
|
||||
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
|
||||
$this->authorize('checkin', $accessory);
|
||||
session()->put('checkedInFrom', $accessory_checkout->assigned_to);
|
||||
session()->put('checkout_to_type', match ($accessory_checkout->assigned_type) {
|
||||
'App\Models\User' => 'user',
|
||||
'App\Models\Location' => 'location',
|
||||
'App\Models\Asset' => 'asset',
|
||||
});
|
||||
|
||||
$this->authorize('checkin', $accessory);
|
||||
$checkin_hours = date('H:i:s');
|
||||
$checkin_at = date('Y-m-d H:i:s');
|
||||
if ($request->filled('checkin_at')) {
|
||||
@@ -65,7 +78,8 @@ class AccessoryCheckinController extends Controller
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
}
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
|
||||
|
||||
@@ -97,7 +97,7 @@ class AccessoryCheckoutController extends Controller
|
||||
|
||||
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Events\CheckoutDeclined;
|
||||
use App\Events\ItemAccepted;
|
||||
use App\Events\ItemDeclined;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\CheckoutAcceptanceResponseMail;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
@@ -20,9 +21,12 @@ use App\Models\License;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Notifications\AcceptanceAssetAcceptedNotification;
|
||||
use App\Notifications\AcceptanceAssetAcceptedToUserNotification;
|
||||
use App\Notifications\AcceptanceAssetDeclinedNotification;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\SettingsController;
|
||||
@@ -148,6 +152,8 @@ class AcceptanceController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$assigned_user = User::find($acceptance->assigned_to_id);
|
||||
// this is horrible
|
||||
switch($acceptance->checkoutable_type){
|
||||
case 'App\Models\Asset':
|
||||
@@ -157,35 +163,30 @@ class AcceptanceController extends Controller
|
||||
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
|
||||
}
|
||||
$display_model = $asset_model->name;
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
|
||||
case 'App\Models\Accessory':
|
||||
$pdf_view_route ='account.accept.accept-accessory-eula';
|
||||
$accessory = Accessory::find($item->id);
|
||||
$display_model = $accessory->name;
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
|
||||
case 'App\Models\LicenseSeat':
|
||||
$pdf_view_route ='account.accept.accept-license-eula';
|
||||
$license = License::find($item->license_id);
|
||||
$display_model = $license->name;
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
|
||||
case 'App\Models\Component':
|
||||
$pdf_view_route ='account.accept.accept-component-eula';
|
||||
$component = Component::find($item->id);
|
||||
$display_model = $component->name;
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
|
||||
case 'App\Models\Consumable':
|
||||
$pdf_view_route ='account.accept.accept-consumable-eula';
|
||||
$consumable = Consumable::find($item->id);
|
||||
$display_model = $consumable->name;
|
||||
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
|
||||
break;
|
||||
}
|
||||
// if ($acceptance->checkoutable_type == 'App\Models\Asset') {
|
||||
@@ -226,7 +227,7 @@ class AcceptanceController extends Controller
|
||||
'note' => $request->input('note'),
|
||||
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
|
||||
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'),
|
||||
'assigned_to' => $assigned_to,
|
||||
'assigned_to' => $assigned_user->present()->fullName,
|
||||
'company_name' => $branding_settings->site_name,
|
||||
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
|
||||
'logo' => $path_logo,
|
||||
@@ -240,6 +241,19 @@ class AcceptanceController extends Controller
|
||||
}
|
||||
|
||||
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
|
||||
|
||||
// Send the PDF to the signing user
|
||||
if (($request->input('send_copy') == '1') && ($assigned_user->email !='')) {
|
||||
|
||||
// Add the attachment for the signing user into the $data array
|
||||
$data['file'] = $pdf_filename;
|
||||
|
||||
try {
|
||||
$assigned_user->notify(new AcceptanceAssetAcceptedToUserNotification($data));
|
||||
} catch (\Exception $e) {
|
||||
Log::warning($e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
|
||||
} catch (\Exception $e) {
|
||||
@@ -337,6 +351,21 @@ class AcceptanceController extends Controller
|
||||
$return_msg = trans('admin/users/message.declined');
|
||||
}
|
||||
|
||||
if ($acceptance->alert_on_response_id) {
|
||||
try {
|
||||
$recipient = User::find($acceptance->alert_on_response_id);
|
||||
|
||||
if ($recipient) {
|
||||
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
|
||||
$acceptance,
|
||||
$recipient,
|
||||
$request->input('asset_acceptance') === 'accepted',
|
||||
));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning($e);
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to('account/accept')->with('success', $return_msg);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class AccessoriesController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$accessories->where('company_id', '=', $request->input('company_id'));
|
||||
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
@@ -249,11 +249,11 @@ class AccessoriesController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
|
||||
$this->authorize($accessory);
|
||||
|
||||
if ($accessory->hasUsers() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()])));
|
||||
if ($accessory->checkouts_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/general.delete_disabled')));
|
||||
}
|
||||
|
||||
$accessory->delete();
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assets')) {
|
||||
Storage::makeDirectory('private_uploads/assets', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
|
||||
|
||||
$asset->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function list($assetId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
// Check that there are some uploads on this asset that can be listed
|
||||
if ($asset->uploads->count() > 0) {
|
||||
$files = array();
|
||||
foreach ($asset->uploads as $upload) {
|
||||
array_push($files, $upload);
|
||||
}
|
||||
// Give the list of files back to the user
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/hardware/message.upload.success')));
|
||||
}
|
||||
|
||||
// There are no files.
|
||||
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/hardware/message.upload.success')));
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function show($assetId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assets/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function destroy($assetId = null, $fileId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,7 @@ class AssetMaintenancesController extends Controller
|
||||
'serial',
|
||||
'created_by',
|
||||
'supplier',
|
||||
'location',
|
||||
'is_warranty',
|
||||
'status_label',
|
||||
];
|
||||
@@ -98,6 +99,9 @@ class AssetMaintenancesController extends Controller
|
||||
case 'serial':
|
||||
$maintenances = $maintenances->OrderByAssetSerial($order);
|
||||
break;
|
||||
case 'location':
|
||||
$maintenances = $maintenances->OrderLocationName($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$maintenances = $maintenances->OrderStatusName($order);
|
||||
break;
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetModelFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assetmodels')) {
|
||||
Storage::makeDirectory('private_uploads/assetmodels', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
|
||||
|
||||
$assetModel->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetmodel
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function list($assetmodel_id) : JsonResponse | array
|
||||
{
|
||||
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
|
||||
$this->authorize('view', $assetmodel);
|
||||
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assetmodels/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$rel_path = 'private_uploads/assetmodels';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,12 @@ class AssetModelsController extends Controller
|
||||
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
|
||||
}
|
||||
|
||||
if ($request->input('requestable') == 'true') {
|
||||
$assetmodels = $assetmodels->where('models.requestable', '=', '1');
|
||||
} elseif ($request->input('requestable') == 'false') {
|
||||
$assetmodels = $assetmodels->where('models.requestable', '=', '0');
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
|
||||
}
|
||||
@@ -148,7 +154,7 @@ class AssetModelsController extends Controller
|
||||
$assetmodel = $request->handleImages($assetmodel);
|
||||
|
||||
if ($assetmodel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
|
||||
|
||||
@@ -201,7 +207,7 @@ class AssetModelsController extends Controller
|
||||
$assetmodel = AssetModel::findOrFail($id);
|
||||
$assetmodel->fill($request->all());
|
||||
$assetmodel = $request->handleImages($assetmodel);
|
||||
|
||||
|
||||
/**
|
||||
* Allow custom_fieldset_id to override and populate fieldset_id.
|
||||
* This is stupid, but required for legacy API support.
|
||||
@@ -216,7 +222,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
|
||||
if ($assetmodel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
|
||||
|
||||
@@ -114,6 +114,7 @@ class AssetsController extends Controller
|
||||
'byod',
|
||||
'asset_eol_date',
|
||||
'requestable',
|
||||
'jobtitle',
|
||||
];
|
||||
|
||||
$filter = [];
|
||||
@@ -298,9 +299,15 @@ class AssetsController extends Controller
|
||||
if ($request->input('requestable') == 'true') {
|
||||
$assets->where('assets.requestable', '=', '1');
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('model_id')) {
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
// If model_id is already an array, just use it as-is
|
||||
if (is_array($request->input('model_id'))) {
|
||||
$assets->InModelList($request->input('model_id'));
|
||||
} else {
|
||||
// Otherwise, turn it into an array
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
@@ -389,6 +396,9 @@ class AssetsController extends Controller
|
||||
case 'assigned_to':
|
||||
$assets->OrderAssigned($order);
|
||||
break;
|
||||
case 'jobtitle':
|
||||
$assets->OrderByJobTitle($order);
|
||||
break;
|
||||
case 'created_by':
|
||||
$assets->OrderByCreatedByName($order);
|
||||
break;
|
||||
@@ -436,12 +446,6 @@ class AssetsController extends Controller
|
||||
}]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Here we're just determining which Transformer (via $transformer) to use based on the
|
||||
* variables we set earlier on in this method - we default to AssetsTransformer.
|
||||
*/
|
||||
return (new $transformer)->transformAssets($assets, $total, $request);
|
||||
}
|
||||
|
||||
@@ -574,7 +578,12 @@ class AssetsController extends Controller
|
||||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id',
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||
])->with('model', 'assetstatus', 'assignedTo')
|
||||
->NotArchived();
|
||||
|
||||
if ((Setting::getSettings()->full_multiple_companies_support=='1') && ($request->filled('companyId'))) {
|
||||
$assets->where('assets.company_id', $request->input('companyId'));
|
||||
}
|
||||
|
||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
@@ -584,7 +593,6 @@ class AssetsController extends Controller
|
||||
$assets = $assets->AssignedSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
$assets = $assets->paginate(50);
|
||||
|
||||
// Loop through and set some custom properties for the transformer to use.
|
||||
@@ -697,7 +705,9 @@ class AssetsController extends Controller
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
|
||||
// below is what we want the _eventual_ return to look like - in a more standardized format.
|
||||
// return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
@@ -1135,12 +1145,12 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Validate custom fields
|
||||
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
// Validate the rest of the data before we turn off the event dispatcher
|
||||
if ($asset->isInvalid()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag' => $asset->asset_tag], $asset->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class CategoriesController extends Controller
|
||||
'notes',
|
||||
])
|
||||
->with('adminuser')
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
|
||||
|
||||
|
||||
/*
|
||||
@@ -212,7 +212,7 @@ class CategoriesController extends Controller
|
||||
public function destroy($id) : JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count')->findOrFail($id);
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
return response()->json(
|
||||
|
||||
@@ -43,7 +43,10 @@ class CompaniesController extends Controller
|
||||
|
||||
$companies = Company::withCount(['assets as assets_count' => function ($query) {
|
||||
$query->AssetsForShow();
|
||||
}])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
|
||||
}])
|
||||
->with('adminuser')
|
||||
->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies->TextSearch($request->input('search'));
|
||||
@@ -119,6 +122,7 @@ class CompaniesController extends Controller
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$this->authorize('view', $company);
|
||||
return (new CompaniesTransformer)->transformCompany($company);
|
||||
|
||||
}
|
||||
@@ -136,6 +140,7 @@ class CompaniesController extends Controller
|
||||
{
|
||||
$this->authorize('update', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$this->authorize('update', $company);
|
||||
$company->fill($request->all());
|
||||
$company = $request->handleImages($company);
|
||||
|
||||
@@ -188,6 +193,7 @@ class CompaniesController extends Controller
|
||||
'companies.image',
|
||||
]);
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class ComponentsController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$components->where('company_id', '=', $request->input('company_id'));
|
||||
$components->where('components.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
|
||||
@@ -40,7 +40,7 @@ class ConsumablesController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$consumables->where('company_id', '=', $request->input('company_id'));
|
||||
$consumables->where('consumables.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
|
||||
@@ -76,7 +76,7 @@ class GroupsController extends Controller
|
||||
$this->authorize('superadmin');
|
||||
$group = new Group;
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$permissions = json_encode(config('permissions'));
|
||||
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
|
||||
|
||||
$group->name = $request->input('name');
|
||||
|
||||
@@ -234,6 +234,15 @@ class ImportController extends Controller
|
||||
case 'location':
|
||||
$redirectTo = 'locations.index';
|
||||
break;
|
||||
case 'supplier':
|
||||
$redirectTo = 'suppliers.index';
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$redirectTo = 'manufacturers.index';
|
||||
break;
|
||||
case 'category':
|
||||
$redirectTo = 'categories.index';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
|
||||
@@ -29,12 +29,21 @@ class LicenseSeatsController extends Controller
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
|
||||
->where('license_seats.license_id', $licenseId);
|
||||
|
||||
if ($request->input('status') == 'available') {
|
||||
$seats->whereNull('license_seats.assigned_to');
|
||||
}
|
||||
|
||||
if ($request->input('status') == 'assigned') {
|
||||
$seats->ByAssigned();
|
||||
}
|
||||
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort') == 'department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} else {
|
||||
$seats->orderBy('id', $order);
|
||||
$seats->orderBy('updated_at', $order);
|
||||
}
|
||||
|
||||
$total = $seats->count();
|
||||
|
||||
@@ -87,7 +87,8 @@ class LocationsController extends Controller
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count');
|
||||
->withCount('users as users_count')
|
||||
->with('adminuser');
|
||||
|
||||
// Only scope locations if the setting is enabled
|
||||
if (Setting::getSettings()->scope_locations_fmcs) {
|
||||
@@ -218,6 +219,7 @@ class LocationsController extends Controller
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency',
|
||||
'locations.company_id',
|
||||
'locations.notes',
|
||||
])
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
|
||||
@@ -4,15 +4,19 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ProfileTransformer;
|
||||
use App\Models\CheckoutRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
@@ -167,6 +171,22 @@ class ProfileController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the EULAs accepted by the user.
|
||||
*
|
||||
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*@since [v8.1.16]
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
*/
|
||||
public function eulas(ProfileTransformer $transformer)
|
||||
{
|
||||
// Only return this user's EULAs
|
||||
$eulas = auth()->user()->eulas;
|
||||
return response()->json(
|
||||
$transformer->transformFiles($eulas, $eulas->count())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Api;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
@@ -18,10 +20,11 @@ class ReportsController extends Controller
|
||||
*/
|
||||
public function index(Request $request) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$this->authorize('activity.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'adminuser', 'target', 'location');
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
@@ -24,10 +24,15 @@ class SuppliersController extends Controller
|
||||
public function index(Request $request): array
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$allowed_columns = ['
|
||||
id',
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'address',
|
||||
'address2',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'phone',
|
||||
'contact',
|
||||
'fax',
|
||||
@@ -39,21 +44,24 @@ class SuppliersController extends Controller
|
||||
'components_count',
|
||||
'consumables_count',
|
||||
'url',
|
||||
'notes',
|
||||
];
|
||||
|
||||
$suppliers = Supplier::select(
|
||||
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes', 'url'])
|
||||
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'created_by', 'updated_at', 'deleted_at', 'image', 'notes', 'url', 'zip'])
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('licenses as licenses_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('components as components_count')
|
||||
->withCount('consumables as consumables_count');
|
||||
->withCount('consumables as consumables_count')
|
||||
->with('adminuser');
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers = $suppliers->TextSearch($request->input('search'));
|
||||
$suppliers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$suppliers->where('name', '=', $request->input('name'));
|
||||
}
|
||||
@@ -100,7 +108,15 @@ class SuppliersController extends Controller
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$suppliers->orderBy($sort, $order);
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'created_by':
|
||||
$suppliers->OrderByCreatedByName($order);
|
||||
break;
|
||||
default:
|
||||
$suppliers->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$total = $suppliers->count();
|
||||
$suppliers = $suppliers->skip($offset)->take($limit)->get();
|
||||
|
||||
259
app/Http/Controllers/Api/UploadedFilesController.php
Normal file
259
app/Http/Controllers/Api/UploadedFilesController.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\UploadedFilesTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
|
||||
class UploadedFilesController extends Controller
|
||||
{
|
||||
|
||||
|
||||
static $map_object_type = [
|
||||
'accessories' => Accessory::class,
|
||||
'assets' => Asset::class,
|
||||
'components' => Component::class,
|
||||
'consumables' => Consumable::class,
|
||||
'hardware' => Asset::class,
|
||||
'licenses' => License::class,
|
||||
'locations' => Location::class,
|
||||
'models' => AssetModel::class,
|
||||
'users' => User::class,
|
||||
];
|
||||
|
||||
static $map_storage_path = [
|
||||
'accessories' => 'private_uploads/accessories/',
|
||||
'assets' => 'private_uploads/assets/',
|
||||
'components' => 'private_uploads/components/',
|
||||
'consumables' => 'private_uploads/consumables/',
|
||||
'hardware' => 'private_uploads/assets/',
|
||||
'licenses' => 'private_uploads/licenses/',
|
||||
'locations' => 'private_uploads/locations/',
|
||||
'models' => 'private_uploads/assetmodels/',
|
||||
'users' => 'private_uploads/users/',
|
||||
];
|
||||
|
||||
static $map_file_prefix= [
|
||||
'accessories' => 'accessory',
|
||||
'assets' => 'asset',
|
||||
'components' => 'component',
|
||||
'consumables' => 'consumable',
|
||||
'hardware' => 'asset',
|
||||
'licenses' => 'license',
|
||||
'locations' => 'location',
|
||||
'models' => 'model',
|
||||
'users' => 'user',
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List files for an object
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to list files for
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function index(Request $request, $object_type, $id) : JsonResponse | array
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// Columns allowed for sorting
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'filename',
|
||||
'action_type',
|
||||
'action_date',
|
||||
'note',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
$uploads = $object->uploads();
|
||||
$offset = ($request->input('offset') > $object->count()) ? $object->count() : abs($request->input('offset'));
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'action_logs.created_at';
|
||||
|
||||
// Text search on action_logs fields
|
||||
// We could use the normal Actionlogs text scope, but it's a very heavy query since it's searcghing across all relations
|
||||
// And we generally won't need that here
|
||||
if ($request->filled('search')) {
|
||||
|
||||
$uploads->where(
|
||||
function ($query) use ($request) {
|
||||
$query->where('filename', 'LIKE', '%' . $request->input('search') . '%')
|
||||
->orWhere('note', 'LIKE', '%' . $request->input('search') . '%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
|
||||
return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to store so we can check permisisons
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $object_type, $id) : JsonResponse
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// If the file storage directory doesn't exist, create it
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type])) {
|
||||
Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
|
||||
}
|
||||
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// Loop over the attached files and add them to the object
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
|
||||
$files[] = $file_name;
|
||||
$object->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
|
||||
->where('item_type', '=', self::$map_object_type[$object_type])
|
||||
->where('item_id', '=', $id)->whereIn('filename', $files)
|
||||
->get();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
|
||||
}
|
||||
|
||||
// No files were submitted
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to delete from so we can check permisisons
|
||||
* @param $file_id the ID of the file to delete from the action_logs table
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check that the file being requested exists for the object
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_type', self::$map_object_type[$object_type])->where('item_id', $object->id)->find($file_id)
|
||||
) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 200);
|
||||
}
|
||||
|
||||
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200));
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to delete from so we can check permisisons
|
||||
* @param $file_id the ID of the file to delete from the action_logs table
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function destroy($object_type, $id, $file_id) : JsonResponse
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('update', self::$map_object_type[$object_type]);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
|
||||
->where('item_id', $object->id)->first();
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
if ($log->delete()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
@@ -22,6 +23,7 @@ use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -80,7 +82,12 @@ class UsersController extends Controller
|
||||
'users.autoassign_licenses',
|
||||
'users.website',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
|
||||
])->with('manager')
|
||||
->with('groups')
|
||||
->with('userloc')
|
||||
->with('company')
|
||||
->with('department')
|
||||
->with('createdBy')
|
||||
->withCount([
|
||||
'assets as assets_count' => function(Builder $query) {
|
||||
$query->withoutTrashed();
|
||||
@@ -206,11 +213,11 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('manages_users_count')) {
|
||||
$users->has('manages_users_count', '=', $request->input('manages_users_count'));
|
||||
$users->has('managesUsers', '=', $request->input('manages_users_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('manages_locations_count')) {
|
||||
$users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
|
||||
$users->has('managedLocations', '=', $request->input('manages_locations_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('autoassign_licenses')) {
|
||||
@@ -339,6 +346,7 @@ class UsersController extends Controller
|
||||
$users = $users->where(function ($query) use ($request) {
|
||||
$query->SimpleNameSearch($request->get('search'))
|
||||
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('email', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
|
||||
});
|
||||
}
|
||||
@@ -473,8 +481,25 @@ class UsersController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
|
||||
}
|
||||
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
// check for permissions related fields and pull them out if the current user cannot edit them
|
||||
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
|
||||
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
if ($request->filled('username')) {
|
||||
$user->username = $request->input('username');
|
||||
}
|
||||
|
||||
if ($request->filled('email')) {
|
||||
$user->email = $request->input('email');
|
||||
}
|
||||
|
||||
if ($request->filled('activated')) {
|
||||
$user->activated = $request->input('activated');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// We need to use has() instead of filled()
|
||||
@@ -675,7 +700,6 @@ class UsersController extends Controller
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
if ($user = User::where('id', $id)->withTrashed()->first()) {
|
||||
$this->authorize('update', $user);
|
||||
$licenses = $user->licenses()->get();
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
@@ -735,6 +759,25 @@ class UsersController extends Controller
|
||||
return (new UsersTransformer)->transformUser($request->user());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the EULAs accepted by the user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*@since [v8.1.16]
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
*/
|
||||
public function eulas(User $user, ActionlogsTransformer $transformer)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$eulas = $user->eulas;
|
||||
return response()->json(
|
||||
$transformer->transformActionlogs($eulas, $eulas->count())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a soft-deleted user.
|
||||
*
|
||||
|
||||
@@ -19,19 +19,6 @@ use \Illuminate\Http\RedirectResponse;
|
||||
*/
|
||||
class AssetMaintenancesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Checks for permissions for this action.
|
||||
*
|
||||
* @todo This should be replaced with middleware and/or policies
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
private static function getInsufficientPermissionsRedirect(): RedirectResponse
|
||||
{
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
@@ -67,16 +54,10 @@ class AssetMaintenancesController extends Controller
|
||||
// We have to set this so that the correct property is set in the select2 ajax dropdown
|
||||
$asset->asset_id = $asset->id;
|
||||
}
|
||||
|
||||
// Prepare Asset Maintenance Type List
|
||||
$assetMaintenanceType = [
|
||||
'' => 'Select an asset maintenance type',
|
||||
] + AssetMaintenance::getImprovementOptions();
|
||||
// Mark the selected asset, if it came in
|
||||
|
||||
|
||||
return view('asset_maintenances/edit')
|
||||
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
|
||||
->with('asset', $asset)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', new AssetMaintenance);
|
||||
}
|
||||
|
||||
@@ -91,43 +72,45 @@ class AssetMaintenancesController extends Controller
|
||||
public function store(Request $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
$asset = Asset::find($request->input('asset_id'));
|
||||
|
||||
if ((! Company::isCurrentUserHasAccess($asset)) && ($asset != null)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
$assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
|
||||
|
||||
// Loop through the selected assets
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $asset->id;
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->created_by = auth()->id();
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if (!$assetMaintenance->save()) {
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->created_by = auth()->id();
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,26 +118,19 @@ class AssetMaintenancesController extends Controller
|
||||
*
|
||||
* @see AssetMaintenancesController::postEdit() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @param int $assetMaintenanceId
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Prepare Improvement Type List
|
||||
$assetMaintenanceType = ['' => 'Select an improvement type'] + AssetMaintenance::getImprovementOptions();
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
|
||||
return view('asset_maintenances/edit')
|
||||
->with('selectedAsset', null)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', $maintenance);
|
||||
->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
|
||||
->with('asset_ids', request()->input('asset_ids', []))
|
||||
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
|
||||
->with('item', $maintenance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,33 +146,21 @@ class AssetMaintenancesController extends Controller
|
||||
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
|
||||
$maintenance->supplier_id = $request->input('supplier_id');
|
||||
$maintenance->is_warranty = $request->input('is_warranty');
|
||||
$maintenance->is_warranty = $request->input('is_warranty', 0);
|
||||
$maintenance->cost = $request->input('cost');
|
||||
$maintenance->notes = $request->input('notes');
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$maintenance->asset_id = $request->input('asset_id');
|
||||
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$maintenance->title = $request->input('title');
|
||||
$maintenance->start_date = $request->input('start_date');
|
||||
$maintenance->completion_date = $request->input('completion_date');
|
||||
|
||||
if (($maintenance->completion_date == null)
|
||||
) {
|
||||
|
||||
// Todo - put this in a getter/setter?
|
||||
if (($maintenance->completion_date == null))
|
||||
{
|
||||
if (($maintenance->asset_maintenance_time !== 0)
|
||||
|| (! is_null($maintenance->asset_maintenance_time))
|
||||
) {
|
||||
@@ -213,10 +177,7 @@ class AssetMaintenancesController extends Controller
|
||||
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($maintenance->save()) {
|
||||
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.edit.success'));
|
||||
}
|
||||
@@ -232,21 +193,12 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function destroy($assetMaintenanceId) : RedirectResponse
|
||||
public function destroy(AssetMaintenance $maintenance) : RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
// Delete the asset maintenance
|
||||
$assetMaintenance->delete();
|
||||
|
||||
$maintenance->delete();
|
||||
// Redirect to the asset_maintenance management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.delete.success'));
|
||||
@@ -262,11 +214,6 @@ class AssetMaintenancesController extends Controller
|
||||
*/
|
||||
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class AssetCheckinController extends Controller
|
||||
{
|
||||
@@ -41,7 +42,19 @@ class AssetCheckinController extends Controller
|
||||
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
return view('hardware/checkin', compact('asset'))
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
$target_option = match ($asset->assigned_type) {
|
||||
'App\Models\Asset' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset_previous')]),
|
||||
'App\Models\Location' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.location')]),
|
||||
default => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.user')]),
|
||||
};
|
||||
return view('hardware/checkin', compact('asset', 'target_option'))
|
||||
->with('item', $asset)
|
||||
->with('statusLabel_list', Helper::statusLabelList())
|
||||
->with('backto', $backto)
|
||||
@@ -75,12 +88,14 @@ class AssetCheckinController extends Controller
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
if ($asset->assignedType() == Asset::USER) {
|
||||
$user = $asset->assignedTo;
|
||||
}
|
||||
session()->put('checkedInFrom', $asset->assignedTo->id);
|
||||
session()->put('checkout_to_type', match ($asset->assigned_type) {
|
||||
'App\Models\User' => 'user',
|
||||
'App\Models\Location' => 'location',
|
||||
'App\Models\Asset' => 'asset',
|
||||
});
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkin = now();
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
$asset->name = $request->get('name');
|
||||
@@ -107,11 +122,14 @@ class AssetCheckinController extends Controller
|
||||
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
// Handle last checkin date
|
||||
$checkin_at = date('Y-m-d H:i:s');
|
||||
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
|
||||
$originalValues['action_date'] = $checkin_at;
|
||||
$checkin_at = $request->get('checkin_at');
|
||||
|
||||
}
|
||||
$asset->last_checkin = $checkin_at;
|
||||
|
||||
$asset->licenseseats->each(function (LicenseSeat $seat) {
|
||||
$seat->update(['assigned_to' => null]);
|
||||
@@ -135,7 +153,8 @@ class AssetCheckinController extends Controller
|
||||
if ($asset->save()) {
|
||||
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')
|
||||
->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());
|
||||
|
||||
@@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class AssetCheckoutController extends Controller
|
||||
{
|
||||
@@ -36,6 +37,14 @@ class AssetCheckoutController extends Controller
|
||||
->with('error', trans('admin/hardware/general.model_invalid_fix'));
|
||||
}
|
||||
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'))
|
||||
->with('statusLabel_list', Helper::deployableStatusLabelList())
|
||||
@@ -114,7 +123,7 @@ class AssetCheckoutController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')
|
||||
->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
|
||||
@@ -149,7 +149,7 @@ class AssetsController extends Controller
|
||||
$asset->byod = request('byod', 0);
|
||||
|
||||
if (! empty($settings->audit_interval)) {
|
||||
$asset->next_audit_date = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
|
||||
}
|
||||
|
||||
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
|
||||
@@ -188,14 +188,31 @@ class AssetsController extends Controller
|
||||
|
||||
// Validate the asset before saving
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
if (request('assigned_user')) {
|
||||
$target = User::find(request('assigned_user'));
|
||||
$target = null;
|
||||
$location = null;
|
||||
|
||||
if ($userId = request('assigned_user')) {
|
||||
$target = User::find($userId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
} elseif (request('assigned_asset')) {
|
||||
$target = Asset::find(request('assigned_asset'));
|
||||
|
||||
} elseif ($assetId = request('assigned_asset')) {
|
||||
$target = Asset::find($assetId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
|
||||
}
|
||||
$location = $target->location_id;
|
||||
} elseif (request('assigned_location')) {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
|
||||
} elseif ($locationId = request('assigned_location')) {
|
||||
$target = Location::find($locationId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
|
||||
}
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
@@ -210,24 +227,27 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'),
|
||||
'checkout_to_type' => $request->get('checkout_to_type'),
|
||||
'other_redirect' => 'model' ]);
|
||||
|
||||
|
||||
|
||||
if ($successes) {
|
||||
if ($failures) {
|
||||
//some succeeded, some failed
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets') //FIXME - not tested
|
||||
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
|
||||
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
|
||||
} else {
|
||||
if (count($successes) == 1) {
|
||||
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
|
||||
//and re-translated
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')
|
||||
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset), 'id', 'tag' => e($asset->asset_tag)]));
|
||||
} else {
|
||||
//multi-success
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')
|
||||
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
|
||||
}
|
||||
}
|
||||
@@ -248,6 +268,7 @@ class AssetsController extends Controller
|
||||
public function edit(Asset $asset) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize($asset);
|
||||
session()->put('back_url', url()->previous());
|
||||
return view('hardware/edit')
|
||||
->with('item', $asset)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
@@ -351,11 +372,6 @@ class AssetsController extends Controller
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update with '.$status->getStatuslabelType().' status', date('Y-m-d H:i:s'), $originalValues));
|
||||
}
|
||||
|
||||
if ($asset->assigned_to == '') {
|
||||
$asset->location_id = $request->input('rtd_location_id', null);
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('image_delete')) {
|
||||
try {
|
||||
unlink(public_path().'/uploads/assets/'.$asset->image);
|
||||
@@ -415,11 +431,15 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
session()->put([
|
||||
'redirect_option' => $request->get('redirect_option'),
|
||||
'checkout_to_type' => $request->get('checkout_to_type'),
|
||||
'other_redirect' => $request->get('redirect_option') === 'other_redirect' ? 'model' : null,
|
||||
]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
if ($asset->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')
|
||||
->with('success', trans('admin/hardware/message.update.success'));
|
||||
}
|
||||
|
||||
@@ -451,7 +471,7 @@ class AssetsController extends Controller
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues));
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update(['assigned_to' => null]);
|
||||
->update(['assigned_to' => null, 'assigned_type' => null]);
|
||||
}
|
||||
|
||||
|
||||
@@ -463,6 +483,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$asset->delete();
|
||||
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
|
||||
@@ -524,7 +545,7 @@ class AssetsController extends Controller
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->qr_code == '1') && ($settings->label2_2d_type !== 'none')) {
|
||||
if ($settings->label2_2d_type !== 'none') {
|
||||
|
||||
if ($asset) {
|
||||
$size = Helper::barcodeDimensions($settings->label2_2d_type);
|
||||
@@ -882,11 +903,20 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
public function audit(Asset $asset)
|
||||
public function audit(Asset $asset): View | RedirectResponse
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$this->authorize('audit', Asset::class);
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
$dt = Carbon::now()->addMonths( (int) $settings->audit_interval)->toDateString();
|
||||
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
@@ -895,6 +925,10 @@ class AssetsController extends Controller
|
||||
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
||||
session()->put('redirect_option', $request->get('redirect_option'));
|
||||
session()->put('other_redirect', 'audit');
|
||||
|
||||
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
@@ -929,8 +963,8 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Validate custom fields
|
||||
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||
// Invoke the validation to see if the audit will complete successfully
|
||||
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
|
||||
|
||||
// Validate the rest of the data before we turn off the event dispatcher
|
||||
if ($asset->isInvalid()) {
|
||||
@@ -970,7 +1004,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
|
||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
return Helper::getRedirectOption($request, $asset->id, 'Assets')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
||||
|
||||
@@ -52,11 +52,26 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
$asset_ids = $request->input('ids');
|
||||
|
||||
if ($request->input('bulk_actions') === 'checkout') {
|
||||
$status_check =$this->hasUndeployableStatus($asset_ids);
|
||||
if($status_check && $status_check['status'] === true){
|
||||
|
||||
$asset_tags = implode(', ', array_column($status_check['tags'], 'asset_tag'));
|
||||
$asset_ids = $status_check['asset_ids'];
|
||||
|
||||
session()->flash('warning', trans('admin/hardware/message.undeployable', ['asset_tags' => $asset_tags]));
|
||||
}
|
||||
|
||||
$request->session()->flashInput(['selected_assets' => $asset_ids]);
|
||||
return redirect()->route('hardware.bulkcheckout.show');
|
||||
}
|
||||
|
||||
if ($request->input('bulk_actions') === 'maintenance') {
|
||||
$request->session()->flashInput(['selected_assets' => $asset_ids]);
|
||||
return redirect()->route('maintenances.create');
|
||||
}
|
||||
|
||||
// Figure out where we need to send the user after the update is complete, and store that in the session
|
||||
$bulk_back_url = request()->headers->get('referer');
|
||||
session(['bulk_back_url' => $bulk_back_url]);
|
||||
@@ -97,11 +112,47 @@ class BulkAssetsController extends Controller
|
||||
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
|
||||
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')
|
||||
$query = Asset::with('assignedTo', 'location', 'model')
|
||||
->whereIn('assets.id', $asset_ids)
|
||||
->withTrashed();
|
||||
|
||||
$assets = $assets->get();
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$query->OrderModels($order);
|
||||
break;
|
||||
case 'model_number':
|
||||
$query->OrderModelNumber($order);
|
||||
break;
|
||||
case 'category':
|
||||
$query->OrderCategory($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$query->OrderManufacturer($order);
|
||||
break;
|
||||
case 'company':
|
||||
$query->OrderCompany($order);
|
||||
break;
|
||||
case 'location':
|
||||
$query->OrderLocation($order);
|
||||
break;
|
||||
case 'rtd_location':
|
||||
$query->OrderRtdLocation($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$query->OrderStatus($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$query->OrderSupplier($order);
|
||||
break;
|
||||
case 'assigned_to':
|
||||
$query->OrderAssigned($order);
|
||||
break;
|
||||
default:
|
||||
$query->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
$assets = $query->get();
|
||||
|
||||
if ($assets->isEmpty()) {
|
||||
Log::debug('No assets were found for the provided IDs', ['ids' => $asset_ids]);
|
||||
@@ -110,6 +161,7 @@ class BulkAssetsController extends Controller
|
||||
|
||||
$models = $assets->unique('model_id');
|
||||
$modelNames = [];
|
||||
|
||||
foreach($models as $model) {
|
||||
$modelNames[] = $model->model->name;
|
||||
}
|
||||
@@ -145,7 +197,6 @@ class BulkAssetsController extends Controller
|
||||
|
||||
case 'edit':
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
return view('hardware/bulk')
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
@@ -154,40 +205,7 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
break;
|
||||
case 'model_number':
|
||||
$assets->OrderModelNumber($order);
|
||||
break;
|
||||
case 'category':
|
||||
$assets->OrderCategory($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$assets->OrderManufacturer($order);
|
||||
break;
|
||||
case 'company':
|
||||
$assets->OrderCompany($order);
|
||||
break;
|
||||
case 'location':
|
||||
$assets->OrderLocation($order);
|
||||
case 'rtd_location':
|
||||
$assets->OrderRtdLocation($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$assets->OrderStatus($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$assets->OrderSupplier($order);
|
||||
break;
|
||||
case 'assigned_to':
|
||||
$assets->OrderAssigned($order);
|
||||
break;
|
||||
default:
|
||||
$assets->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return redirect()->back()->with('error', 'No action selected');
|
||||
}
|
||||
@@ -206,14 +224,26 @@ class BulkAssetsController extends Controller
|
||||
$error_array = array();
|
||||
|
||||
// Get the back url from the session and then destroy the session
|
||||
$bulk_back_url = route('hardware.index');
|
||||
|
||||
if ($request->session()->has('bulk_back_url')) {
|
||||
$bulk_back_url = $request->session()->pull('bulk_back_url');
|
||||
}
|
||||
$bulk_back_url = $request->session()->pull('bulk_back_url', url()->previous());
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
// find custom field input attributes that start with 'null_'
|
||||
$null_custom_fields_inputs = array_filter($request->all(), function ($key) {
|
||||
// filter out all keys that start with 'null_'
|
||||
return (strpos($key, 'null_') === 0);
|
||||
}, ARRAY_FILTER_USE_KEY);;
|
||||
// remove 'null' from the keys
|
||||
$custom_fields_to_null = [];
|
||||
foreach ($null_custom_fields_inputs as $key => $value) {
|
||||
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
@@ -251,7 +281,9 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('null_expected_checkin_date'))
|
||||
|| ($request->filled('null_next_audit_date'))
|
||||
|| ($request->filled('null_asset_eol_date'))
|
||||
|| ($request->filled('null_notes'))
|
||||
|| ($request->anyFilled($custom_field_columns))
|
||||
|| ($request->anyFilled(array_keys($null_custom_fields_inputs)))
|
||||
|
||||
) {
|
||||
// Let's loop through those assets and build an update array
|
||||
@@ -274,10 +306,14 @@ class BulkAssetsController extends Controller
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months')
|
||||
->conditionallyAddItem('next_audit_date')
|
||||
->conditionallyAddItem('asset_eol_date');
|
||||
->conditionallyAddItem('asset_eol_date')
|
||||
->conditionallyAddItem('notes');
|
||||
foreach ($custom_field_columns as $key => $custom_field_column) {
|
||||
$this->conditionallyAddItem($custom_field_column);
|
||||
}
|
||||
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
|
||||
$this->conditionallyAddItem($key);
|
||||
}
|
||||
|
||||
if (!($asset->eol_explicit)) {
|
||||
if ($request->filled('model_id')) {
|
||||
@@ -328,6 +364,10 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->input('null_notes')=='1') {
|
||||
$this->update_array['notes'] = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($request->filled('purchase_cost')) {
|
||||
@@ -368,10 +408,12 @@ class BulkAssetsController extends Controller
|
||||
// This could probably be added to a form request.
|
||||
// If the asset isn't assigned, we don't care what the status is.
|
||||
// Otherwise we need to make sure the status type is still a deployable one.
|
||||
if (
|
||||
($asset->assigned_to == '')
|
||||
|| ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
|
||||
) {
|
||||
|
||||
$unassigned = $asset->assigned_to == '';
|
||||
$deployable = $updated_status->deployable == '1' && $asset->assetstatus?->deployable == '1';
|
||||
$pending = $updated_status->pending === 1;
|
||||
|
||||
if ($unassigned || $deployable || $pending) {
|
||||
$this->update_array['status_id'] = $updated_status->id;
|
||||
}
|
||||
|
||||
@@ -423,6 +465,7 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start all the custom fields shenanigans
|
||||
*/
|
||||
|
||||
@@ -430,6 +473,15 @@ class BulkAssetsController extends Controller
|
||||
if ($asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
// null custom fields
|
||||
if ($custom_fields_to_null) {
|
||||
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
|
||||
if ($field->db_column == $key) {
|
||||
$this->update_array[$field->db_column] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
if (Gate::allows('admin')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
@@ -488,7 +540,13 @@ class BulkAssetsController extends Controller
|
||||
} // end asset foreach
|
||||
|
||||
if ($has_errors > 0) {
|
||||
return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array);
|
||||
session()->put('bulkedit_ids', $request->input('ids'));
|
||||
session()->put('bulk_asset_errors',$error_array);
|
||||
|
||||
return redirect()
|
||||
->route('hardware.bulkedit')
|
||||
->with('bulk_asset_errors', $error_array)
|
||||
->withInput();
|
||||
}
|
||||
|
||||
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success'));
|
||||
@@ -562,7 +620,10 @@ class BulkAssetsController extends Controller
|
||||
public function showCheckout() : View
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
return view('hardware/bulk-checkout');
|
||||
|
||||
$do_not_change = ['' => trans('general.do_not_change')];
|
||||
$status_label_list = $do_not_change + Helper::deployableStatusLabelList();
|
||||
return view('hardware/bulk-checkout')->with('statusLabel_list', $status_label_list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,13 +655,13 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
$checkout_at = date('Y-m-d H:i:s');
|
||||
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
|
||||
$checkout_at = e($request->get('checkout_at'));
|
||||
$checkout_at = $request->get('checkout_at');
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$expected_checkin = e($request->get('expected_checkin'));
|
||||
$expected_checkin = $request->get('expected_checkin');
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
@@ -608,6 +669,11 @@ class BulkAssetsController extends Controller
|
||||
foreach ($assets as $asset) {
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
// See if there is a status label passed
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->get('status_id');
|
||||
}
|
||||
|
||||
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
|
||||
|
||||
//TODO - I think this logic is duplicated in the checkOut method?
|
||||
@@ -651,4 +717,54 @@ class BulkAssetsController extends Controller
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
|
||||
}
|
||||
}
|
||||
public function hasUndeployableStatus (array $asset_ids)
|
||||
{
|
||||
$undeployable = Asset::whereIn('id', $asset_ids)
|
||||
->undeployable()
|
||||
->get();
|
||||
|
||||
$undeployableTags = $undeployable->map(function ($asset) {
|
||||
return [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
$undeployableIds = array_column($undeployableTags, 'id');
|
||||
$filtered_ids = array_diff($asset_ids, $undeployableIds);
|
||||
|
||||
if($undeployable->isNotEmpty()) {
|
||||
return ['status' => true, 'tags' => $undeployableTags, 'asset_ids' => $filtered_ids];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function bulkEditForm(): View|RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
$asset_ids = session()->pull('bulkedit_ids', []);
|
||||
|
||||
if (empty($asset_ids)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
||||
$assets = Asset::with('model')->withTrashed()->whereIn('id', $asset_ids)->get();
|
||||
|
||||
if ($assets->isEmpty()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.assets_do_not_exist_or_are_invalid'));
|
||||
}
|
||||
|
||||
$models = $assets->unique('model_id');
|
||||
$modelNames = [];
|
||||
foreach ($models as $model) {
|
||||
$modelNames[] = $model->model->name;
|
||||
}
|
||||
|
||||
return view('hardware/bulk')
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('models', $models->pluck(['model']))
|
||||
->with('modelNames', $modelNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ class LoginController extends Controller
|
||||
|
||||
$strip_prefixes = [
|
||||
// IIS/AD
|
||||
// https://github.com/snipe/snipe-it/pull/5862
|
||||
// https://github.com/grokability/snipe-it/pull/5862
|
||||
'\\',
|
||||
|
||||
// Google Cloud IAP
|
||||
@@ -484,6 +484,7 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
$request->session()->regenerate(true);
|
||||
$request->session()->forget('2fa_authed');
|
||||
|
||||
if ($request->session()->has('password_hash_'.Auth::getDefaultDriver())){
|
||||
$request->session()->remove('password_hash_'.Auth::getDefaultDriver());
|
||||
|
||||
@@ -68,6 +68,7 @@ class CategoriesController extends Controller
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->alert_on_response = $request->input('alert_on_response', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->notes = $request->input('notes');
|
||||
$category->created_by = auth()->id();
|
||||
@@ -121,6 +122,7 @@ class CategoriesController extends Controller
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->alert_on_response = $request->input('alert_on_response', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->notes = $request->input('notes');
|
||||
|
||||
@@ -145,7 +147,7 @@ class CategoriesController extends Controller
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::findOrFail($categoryId))) {
|
||||
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
|
||||
}
|
||||
|
||||
@@ -155,7 +157,6 @@ class CategoriesController extends Controller
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
// Redirect to the locations management page
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -123,11 +123,13 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function destroy($companyId) : RedirectResponse
|
||||
{
|
||||
|
||||
if (is_null($company = Company::find($companyId))) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
$this->authorize('delete', $company);
|
||||
if (! $company->isDeletable()) {
|
||||
return redirect()->route('companies.index')
|
||||
|
||||
@@ -100,8 +100,8 @@ class ComponentCheckinController extends Controller
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success',
|
||||
trans('admin/components/message.checkin.success'));
|
||||
return Helper::getRedirectOption($request, $component->id, 'Components')
|
||||
->with('success', trans('admin/components/message.checkin.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
|
||||
@@ -120,6 +120,7 @@ class ComponentCheckoutController extends Controller
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.checkout.success'));
|
||||
return Helper::getRedirectOption($request, $component->id, 'Components')
|
||||
->with('success', trans('admin/components/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ class ComponentsController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
|
||||
return Helper::getRedirectOption($request, $component->id, 'Components')
|
||||
->with('success', trans('admin/components/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
@@ -111,6 +112,7 @@ class ComponentsController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('update', $component);
|
||||
session()->put('back_url', url()->previous());
|
||||
return view('components/edit')
|
||||
->with('item', $component)
|
||||
->with('category_type', 'component');
|
||||
@@ -164,7 +166,8 @@ class ComponentsController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
|
||||
return Helper::getRedirectOption($request, $component->id, 'Components')
|
||||
->with('success', trans('admin/components/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
|
||||
@@ -111,6 +111,7 @@ class ConsumableCheckoutController extends Controller
|
||||
|
||||
|
||||
// Redirect to the new consumable page
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
|
||||
->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ class ConsumablesController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
|
||||
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
|
||||
->with('success', trans('admin/consumables/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
@@ -107,6 +108,7 @@ class ConsumablesController extends Controller
|
||||
public function edit(Consumable $consumable) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize($consumable);
|
||||
session()->put('back_url', url()->previous());
|
||||
return view('consumables/edit')
|
||||
->with('item', $consumable)
|
||||
->with('category_type', 'consumable');
|
||||
@@ -160,7 +162,8 @@ class ConsumablesController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
|
||||
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
|
||||
->with('success', trans('admin/consumables/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
|
||||
@@ -83,30 +83,30 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$show_in_email = $request->get("show_in_email", 0);
|
||||
$display_in_user_view = $request->get("display_in_user_view", 0);
|
||||
$show_in_email = $request->input("show_in_email", 0);
|
||||
$display_in_user_view = $request->input("display_in_user_view", 0);
|
||||
|
||||
// Override the display settings if the field is encrypted
|
||||
if ($request->get("field_encrypted") == '1') {
|
||||
if ($request->input("field_encrypted") == '1') {
|
||||
$show_in_email = '0';
|
||||
$display_in_user_view = '0';
|
||||
}
|
||||
|
||||
|
||||
$field = new CustomField([
|
||||
"name" => trim($request->get("name")),
|
||||
"element" => $request->get("element"),
|
||||
"help_text" => $request->get("help_text"),
|
||||
"field_values" => $request->get("field_values"),
|
||||
"field_encrypted" => $request->get("field_encrypted", 0),
|
||||
"name" => trim($request->input("name")),
|
||||
"element" => $request->input("element"),
|
||||
"help_text" => $request->input("help_text"),
|
||||
"field_values" => $request->input("field_values"),
|
||||
"field_encrypted" => $request->input("field_encrypted", 0),
|
||||
"show_in_email" => $show_in_email,
|
||||
"is_unique" => $request->get("is_unique", 0),
|
||||
"is_unique" => $request->input("is_unique", 0),
|
||||
"display_in_user_view" => $display_in_user_view,
|
||||
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
|
||||
"show_in_listview" => $request->get("show_in_listview", 0),
|
||||
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
|
||||
"display_checkin" => $request->get("display_checkin", 0),
|
||||
"display_checkout" => $request->get("display_checkout", 0),
|
||||
"display_audit" => $request->get("display_audit", 0),
|
||||
"auto_add_to_fieldsets" => $request->input("auto_add_to_fieldsets", 0),
|
||||
"show_in_listview" => $request->input("show_in_listview", 0),
|
||||
"show_in_requestable_list" => $request->input("show_in_requestable_list", 0),
|
||||
"display_checkin" => $request->input("display_checkin", 0),
|
||||
"display_checkout" => $request->input("display_checkout", 0),
|
||||
"display_audit" => $request->input("display_audit", 0),
|
||||
"created_by" => auth()->id()
|
||||
]);
|
||||
|
||||
@@ -238,8 +238,8 @@ class CustomFieldsController extends Controller
|
||||
$display_in_user_view = '0';
|
||||
}
|
||||
|
||||
$field->name = trim(e($request->get("name")));
|
||||
$field->element = e($request->get("element"));
|
||||
$field->name = trim($request->get("name"));
|
||||
$field->element = $request->get("element");
|
||||
$field->field_values = $request->get("field_values");
|
||||
$field->created_by = auth()->id();
|
||||
$field->help_text = $request->get("help_text");
|
||||
@@ -254,9 +254,9 @@ class CustomFieldsController extends Controller
|
||||
$field->display_audit = $request->get("display_audit", 0);
|
||||
|
||||
if ($request->get('format') == 'CUSTOM REGEX') {
|
||||
$field->format = e($request->get('custom_format'));
|
||||
$field->format = $request->get('custom_format');
|
||||
} else {
|
||||
$field->format = e($request->get('format'));
|
||||
$field->format = $request->get('format');
|
||||
}
|
||||
|
||||
if ($field->element == 'checkbox' || $field->element == 'radio'){
|
||||
|
||||
@@ -86,7 +86,8 @@ class LicenseCheckinController extends Controller
|
||||
}
|
||||
|
||||
if($licenseSeat->assigned_to != null){
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
$return_to = User::withTrashed()->find($licenseSeat->assigned_to);
|
||||
session()->put('checkedInFrom', $return_to->id);
|
||||
} else {
|
||||
$return_to = Asset::find($licenseSeat->asset_id);
|
||||
}
|
||||
@@ -97,14 +98,17 @@ class LicenseCheckinController extends Controller
|
||||
$licenseSeat->notes = $request->input('notes');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($request->get('redirect_option') === 'target'){
|
||||
session()->put(['checkout_to_type' => 'user']);
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
|
||||
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
return Helper::getRedirectOption($request, $license->id, 'Licenses')
|
||||
->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
}
|
||||
|
||||
// Redirect to the license page with error
|
||||
|
||||
@@ -89,7 +89,8 @@ class LicenseCheckoutController extends Controller
|
||||
|
||||
|
||||
if ($checkoutTarget) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
return Helper::getRedirectOption($request, $license->id, 'Licenses')
|
||||
->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ class LicensesController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($license->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
|
||||
return Helper::getRedirectOption($request, $license->id, 'Licenses')
|
||||
->with('success', trans('admin/licenses/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($license->getErrors());
|
||||
@@ -125,7 +126,7 @@ class LicensesController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('update', $license);
|
||||
|
||||
session()->put('back_url', url()->previous());
|
||||
$maintained_list = [
|
||||
'' => 'Maintained',
|
||||
'1' => 'Yes',
|
||||
@@ -181,7 +182,8 @@ class LicensesController extends Controller
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($license->save()) {
|
||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.update.success'));
|
||||
return Helper::getRedirectOption($request, $license->id, 'Licenses')
|
||||
->with('success', trans('admin/licenses/message.update.success'));
|
||||
}
|
||||
// If we can't adjust the number of seats, the error is flashed to the session by the event handler in License.php
|
||||
return redirect()->back()->withInput()->withErrors($license->getErrors());
|
||||
|
||||
@@ -66,6 +66,7 @@ class LocationsController extends Controller
|
||||
public function store(ImageUploadRequest $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
|
||||
$location = new Location();
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
@@ -150,7 +151,7 @@ class LocationsController extends Controller
|
||||
if (Setting::getSettings()->scope_locations_fmcs) {
|
||||
$location->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
||||
// check if there are related objects with different company
|
||||
if (Helper::test_locations_fmcs(false, $locationId, $location->company_id)) {
|
||||
if (Helper::test_locations_fmcs(false, $location->id, $location->company_id)) {
|
||||
return redirect()->back()->withInput()->withInput()->with('error', 'error scoped locations');
|
||||
}
|
||||
} else {
|
||||
@@ -176,6 +177,7 @@ class LocationsController extends Controller
|
||||
public function destroy($locationId) : RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
@@ -212,6 +214,8 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function show(Location $location) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
|
||||
$location = Location::withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
@@ -229,6 +233,8 @@ class LocationsController extends Controller
|
||||
|
||||
public function print_assigned($id) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
|
||||
if ($location = Location::where('id', $id)->first()) {
|
||||
$parent = Location::where('id', $location->parent_id)->first();
|
||||
$manager = User::where('id', $location->manager_id)->first();
|
||||
@@ -313,6 +319,7 @@ class LocationsController extends Controller
|
||||
}
|
||||
public function print_all_assigned($id) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
if ($location = Location::where('id', $id)->first()) {
|
||||
$parent = Location::where('id', $location->parent_id)->first();
|
||||
$manager = User::where('id', $location->manager_id)->first();
|
||||
@@ -339,6 +346,8 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function postBulkDelete(Request $request) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Location::class);
|
||||
|
||||
$locations_raw_array = $request->input('ids');
|
||||
|
||||
// Make sure some IDs have been selected
|
||||
@@ -372,6 +381,8 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function postBulkDeleteStore(Request $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
|
||||
$locations_raw_array = $request->input('ids');
|
||||
|
||||
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||
|
||||
111
app/Http/Controllers/LocationsFilesController.php
Normal file
111
app/Http/Controllers/LocationsFilesController.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use \Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class LocationsFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Upload a file to the server.
|
||||
*
|
||||
* @param UploadFileRequest $request
|
||||
* @param int $modelId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*@since [v1.0]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, Location $location) : RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $location);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
|
||||
if (! Storage::exists('private_uploads/locations')) {
|
||||
Storage::makeDirectory('private_uploads/locations', 775);
|
||||
}
|
||||
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/locations/','location-'.$location->id, $file);
|
||||
$location->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $modelId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function show(Location $location, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
|
||||
{
|
||||
|
||||
$this->authorize('view', $location);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
return redirect()->back()->withFragment('files')->with('error', 'No matching file record');
|
||||
}
|
||||
|
||||
$file = 'private_uploads/locations/'.$log->filename;
|
||||
|
||||
if (! Storage::exists($file)) {
|
||||
return redirect()->back()->withFragment('files')->with('error', 'No matching file on server');
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $modelId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function destroy(Location $location, $fileId = null) : RedirectResponse
|
||||
{
|
||||
$rel_path = 'private_uploads/locations';
|
||||
$this->authorize('update', $location);
|
||||
$log = Actionlog::find($fileId);
|
||||
|
||||
if ($log) {
|
||||
|
||||
// This should be moved to purge
|
||||
// if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
// Storage::delete($rel_path.'/'.$log->filename);
|
||||
// }
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -31,7 +32,30 @@ class ManufacturersController extends Controller
|
||||
public function index() : View
|
||||
{
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
return view('manufacturers/index');
|
||||
$manufacturer_count = Manufacturer::withTrashed()->count();
|
||||
return view('manufacturers/index')->with('manufacturer_count', $manufacturer_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the manufacturers listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see Api\ManufacturersController::index() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function seed() : RedirectResponse
|
||||
{
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
|
||||
$manufacturers_count = Manufacturer::withTrashed()->count();
|
||||
|
||||
if ($manufacturers_count == 0) {
|
||||
Artisan::call('db:seed', ['--class' => 'ManufacturerSeeder']);
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('general.seeding.manufacturers.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('manufacturers.index')->with('error', trans_choice('general.seeding.manufacturers.error', ['count' => $manufacturers_count]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,15 +3,21 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\ProfileTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Profiles for
|
||||
* the Snipe-IT Asset Management application.
|
||||
@@ -53,7 +59,7 @@ class ProfileController extends Controller
|
||||
$user->enable_confetti = $request->input('enable_confetti', false);
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en-US');
|
||||
$user->locale = $request->input('locale');
|
||||
}
|
||||
|
||||
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled == '1') && (! config('app.lock_passwords')))) {
|
||||
@@ -136,7 +142,7 @@ class ProfileController extends Controller
|
||||
}
|
||||
|
||||
// This checks to make sure that the user's password isn't the same as their username,
|
||||
// email address, first name or last name (see https://github.com/snipe/snipe-it/issues/8661)
|
||||
// email address, first name or last name (see https://github.com/grokability/snipe-it/issues/8661)
|
||||
// While this is handled via SaveUserRequest form request in other places, we have to do this manually
|
||||
// here because we don't have the username, etc form fields available in the profile password change
|
||||
// form.
|
||||
@@ -220,7 +226,7 @@ class ProfileController extends Controller
|
||||
|
||||
if (!$user = User::find(auth()->id())) {
|
||||
return redirect()->back()
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
|
||||
}
|
||||
if (empty($user->email)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
@@ -234,4 +240,28 @@ class ProfileController extends Controller
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
|
||||
{
|
||||
|
||||
$logentry = Actionlog::where('filename', $filename)->first();
|
||||
|
||||
// Make sure the user has permission to view this file
|
||||
if (auth()->id() != $logentry->target_id) {
|
||||
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
|
||||
}
|
||||
|
||||
if (config('filesystems.default') == 's3_private') {
|
||||
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
|
||||
}
|
||||
|
||||
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
|
||||
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class ReportsController extends Controller
|
||||
$currency = e(Setting::getSettings()->default_currency);
|
||||
}
|
||||
|
||||
$row[] = $asset->purchase_date;
|
||||
$row[] = Helper::getFormattedDateObject($asset->purchase_date, 'date', false);
|
||||
$row[] = $currency.Helper::formatCurrencyOutput($asset->purchase_cost);
|
||||
$row[] = $currency.Helper::formatCurrencyOutput($asset->getDepreciatedValue());
|
||||
$row[] = $currency.Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
|
||||
@@ -485,7 +485,7 @@ class ReportsController extends Controller
|
||||
$header[] = trans('admin/hardware/table.purchase_date');
|
||||
}
|
||||
|
||||
if (($request->filled('purchase_cost')) || ($request->filled('depreciation'))) {
|
||||
if ($request->filled('purchase_cost')) {
|
||||
$header[] = trans('admin/hardware/table.purchase_cost');
|
||||
}
|
||||
|
||||
@@ -737,6 +737,11 @@ class ReportsController extends Controller
|
||||
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
|
||||
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('last_updated_start')) && ($request->filled('last_updated_end'))) {
|
||||
$assets->whereBetween('assets.updated_at', [$request->input('last_updated_start'), $request->input('last_updated_end')]);
|
||||
}
|
||||
|
||||
if ($request->filled('exclude_archived')) {
|
||||
$assets->notArchived();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Http\Requests\StoreLabelSettings;
|
||||
use App\Http\Requests\StoreSecuritySettings;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Group;
|
||||
use App\Models\Labels\Label as LabelModel;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
@@ -290,7 +291,6 @@ class SettingsController extends Controller
|
||||
public function getSettings() : View
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
return view('settings/general', compact('setting'));
|
||||
}
|
||||
|
||||
@@ -352,6 +352,7 @@ class SettingsController extends Controller
|
||||
$setting->dash_chart_type = $request->input('dash_chart_type');
|
||||
$setting->profile_edit = $request->input('profile_edit', 0);
|
||||
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
|
||||
$setting->manager_view_enabled = $request->input('manager_view_enabled', 0);
|
||||
|
||||
|
||||
if ($request->input('per_page') != '') {
|
||||
@@ -650,6 +651,7 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->alert_email = $alert_email;
|
||||
$setting->admin_cc_email = $admin_cc_email;
|
||||
$setting->admin_cc_always = $request->validated('admin_cc_always');
|
||||
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
|
||||
$setting->alert_interval = $request->input('alert_interval');
|
||||
$setting->alert_threshold = $request->input('alert_threshold');
|
||||
@@ -772,6 +774,7 @@ class SettingsController extends Controller
|
||||
$setting->label2_2d_type = $request->input('label2_2d_type');
|
||||
$setting->label2_2d_target = $request->input('label2_2d_target');
|
||||
$setting->label2_fields = $request->input('label2_fields');
|
||||
$setting->label2_empty_row_count = $request->input('label2_empty_row_count');
|
||||
$setting->labels_per_page = $request->input('labels_per_page');
|
||||
$setting->labels_width = $request->input('labels_width');
|
||||
$setting->labels_height = $request->input('labels_height');
|
||||
@@ -921,7 +924,7 @@ class SettingsController extends Controller
|
||||
* @since v5.0.0
|
||||
*/
|
||||
public function postSamlSettings(SettingsSamlRequest $request) : RedirectResponse
|
||||
{
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
@@ -298,7 +298,6 @@ class BulkUsersController extends Controller
|
||||
$this->logItemCheckinAndDelete($assets, Asset::class);
|
||||
$this->logAccessoriesCheckin($accessoryUserRows);
|
||||
$this->logItemCheckinAndDelete($licenses, License::class);
|
||||
$this->logConsumablesCheckin($consumableUserRows);
|
||||
|
||||
Asset::whereIn('id', $assets->pluck('id'))->update([
|
||||
'status_id' => e(request('status_id')),
|
||||
@@ -366,20 +365,6 @@ class BulkUsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
private function logConsumablesCheckin(Collection $consumableUserRows): void
|
||||
{
|
||||
foreach ($consumableUserRows as $consumableUserRow) {
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $consumableUserRow->consumable_id;
|
||||
$logAction->item_type = Consumable::class;
|
||||
$logAction->target_id = $consumableUserRow->assigned_to;
|
||||
$logAction->target_type = User::class;
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->note = 'Bulk checkin items';
|
||||
$logAction->logaction('checkin from');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk-edited users
|
||||
*
|
||||
|
||||
@@ -13,15 +13,8 @@ use App\Models\Company;
|
||||
use App\Models\Group;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Redirect;
|
||||
use Str;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use App\Notifications\CurrentInventory;
|
||||
|
||||
@@ -130,7 +123,7 @@ class UsersController extends Controller
|
||||
}
|
||||
$user->permissions = json_encode($permissions_array);
|
||||
|
||||
// we have to invoke the
|
||||
// we have to invoke the form request here to handle image uploads
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
@@ -142,19 +135,8 @@ class UsersController extends Controller
|
||||
$user->groups()->sync([]);
|
||||
}
|
||||
|
||||
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
|
||||
// Send the credentials through email
|
||||
$data = [];
|
||||
$data['email'] = e($request->input('email'));
|
||||
$data['username'] = e($request->input('username'));
|
||||
$data['first_name'] = e($request->input('first_name'));
|
||||
$data['last_name'] = e($request->input('last_name'));
|
||||
$data['password'] = e($request->input('password'));
|
||||
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
|
||||
return Helper::getRedirectOption($request, $user->id, 'Users')
|
||||
->with('success', trans('admin/users/message.success.create'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
@@ -178,7 +160,7 @@ class UsersController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $permissions
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
||||
* @internal param int $id
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
@@ -186,10 +168,15 @@ class UsersController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('update', User::class);
|
||||
session()->put('back_url', url()->previous());
|
||||
$user = User::with(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed()->find($user->id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
if ($user->trashed()) {
|
||||
return redirect()->route('users.show', $user->id);
|
||||
}
|
||||
|
||||
$permissions = config('permissions');
|
||||
$groups = Group::pluck('name', 'id');
|
||||
|
||||
@@ -242,20 +229,14 @@ class UsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Only save groups if the user is a superuser
|
||||
if (auth()->user()->isSuperUser()) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
|
||||
// Update the user fields
|
||||
$user->username = trim($request->input('username'));
|
||||
$user->email = trim($request->input('email'));
|
||||
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle', null);
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
@@ -267,8 +248,6 @@ class UsersController extends Controller
|
||||
$user->city = $request->input('city', null);
|
||||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
// if a user is editing themselves we should always keep activated true
|
||||
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
$user->remote = $request->input('remote', 0);
|
||||
$user->vip = $request->input('vip', 0);
|
||||
@@ -277,30 +256,49 @@ class UsersController extends Controller
|
||||
$user->end_date = $request->input('end_date', null);
|
||||
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
|
||||
|
||||
// Set this here so that we can overwrite it later if the user is an admin or superadmin
|
||||
$user->activated = $request->input('activated', auth()->user()->is($user) ? 1 : $user->activated);
|
||||
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)
|
||||
->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
// Do we want to update the user password?
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
// check for permissions related fields and only set them if the user has permission to edit them
|
||||
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
|
||||
|
||||
$user->username = trim($request->input('username'));
|
||||
$user->email = trim($request->input('email'));
|
||||
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
|
||||
|
||||
// Do we want to update the user password?
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
$permissions_array = $request->input('permission');
|
||||
|
||||
// Strip out the superuser permission if the user isn't a superadmin
|
||||
if (! auth()->user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
$permissions_array['superuser'] = $orig_superuser;
|
||||
}
|
||||
|
||||
$user->permissions = json_encode($permissions_array);
|
||||
|
||||
// Only save groups if the user is a superuser
|
||||
if (auth()->user()->isSuperUser()) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)
|
||||
->update(['location_id' => $user->location_id]);
|
||||
|
||||
$permissions_array = $request->input('permission');
|
||||
|
||||
// Strip out the superuser permission if the user isn't a superadmin
|
||||
if (! auth()->user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
$permissions_array['superuser'] = $orig_superuser;
|
||||
}
|
||||
|
||||
$user->permissions = json_encode($permissions_array);
|
||||
|
||||
// Handle uploaded avatar
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
@@ -308,7 +306,7 @@ class UsersController extends Controller
|
||||
|
||||
if ($user->save()) {
|
||||
// Redirect to the user page
|
||||
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
|
||||
return Helper::getRedirectOption($request, $user->id, 'Users')
|
||||
->with('success', trans('admin/users/message.success.update'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
@@ -506,6 +504,8 @@ class UsersController extends Controller
|
||||
trans('admin/companies/table.title'),
|
||||
trans('admin/users/table.title'),
|
||||
trans('general.employee_number'),
|
||||
trans('admin/users/table.first_name'),
|
||||
trans('admin/users/table.last_name'),
|
||||
trans('admin/users/table.name'),
|
||||
trans('admin/users/table.username'),
|
||||
trans('admin/users/table.email'),
|
||||
@@ -551,6 +551,8 @@ class UsersController extends Controller
|
||||
($user->company) ? $user->company->name : '',
|
||||
$user->jobtitle,
|
||||
$user->employee_num,
|
||||
$user->first_name,
|
||||
$user->last_name,
|
||||
$user->present()->fullName(),
|
||||
$user->username,
|
||||
$user->email,
|
||||
@@ -594,18 +596,18 @@ class UsersController extends Controller
|
||||
|
||||
$user = User::where('id', $id)
|
||||
->with([
|
||||
'assets.assetlog',
|
||||
'assets.assignedAssets.assetlog',
|
||||
'assets.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
|
||||
'assets.assignedAssets.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
|
||||
'assets.assignedAssets.defaultLoc',
|
||||
'assets.assignedAssets.location',
|
||||
'assets.assignedAssets.model.category',
|
||||
'assets.defaultLoc',
|
||||
'assets.location',
|
||||
'assets.model.category',
|
||||
'accessories.assetlog',
|
||||
'accessories.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
|
||||
'accessories.category',
|
||||
'accessories.manufacturer',
|
||||
'consumables.assetlog',
|
||||
'consumables.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
|
||||
'consumables.category',
|
||||
'consumables.manufacturer',
|
||||
'licenses.category',
|
||||
|
||||
@@ -27,50 +27,126 @@ use Exception;
|
||||
class ViewAssetsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Redirect to the profile page.
|
||||
* Extract custom fields that should be displayed in user view.
|
||||
*
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
private function extractCustomFields(User $user): array
|
||||
{
|
||||
$fieldArray = [];
|
||||
foreach ($user->assets as $asset) {
|
||||
if ($asset->model && $asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
if ($field->display_in_user_view == '1') {
|
||||
$fieldArray[$field->db_column] = $field->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_unique($fieldArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of users viewable by the current user.
|
||||
*
|
||||
* @param User $authUser
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function getViewableUsers(User $authUser): \Illuminate\Support\Collection
|
||||
{
|
||||
// SuperAdmin sees all users
|
||||
if ($authUser->isSuperUser()) {
|
||||
return User::select('id', 'first_name', 'last_name', 'username')
|
||||
->where('activated', 1)
|
||||
->orderBy('last_name')
|
||||
->orderBy('first_name')
|
||||
->get();
|
||||
}
|
||||
|
||||
// Regular manager sees only their subordinates + self
|
||||
$managedUsers = $authUser->getAllSubordinates();
|
||||
|
||||
// If user has subordinates, show them with self at beginning
|
||||
if ($managedUsers->count() > 0) {
|
||||
return collect([$authUser])->merge($managedUsers)
|
||||
->sortBy('last_name')
|
||||
->sortBy('first_name');
|
||||
}
|
||||
|
||||
// User has no subordinates, only sees themselves
|
||||
return collect([$authUser]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected user ID from request or default to current user.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Illuminate\Support\Collection $subordinates
|
||||
* @param int $defaultUserId
|
||||
* @return int
|
||||
*/
|
||||
private function getSelectedUserId(Request $request, \Illuminate\Support\Collection $subordinates, int $defaultUserId): int
|
||||
{
|
||||
// If no subordinates or no user_id in request, return default
|
||||
if ($subordinates->count() <= 1 || !$request->filled('user_id')) {
|
||||
return $defaultUserId;
|
||||
}
|
||||
|
||||
$requestedUserId = (int) $request->input('user_id');
|
||||
|
||||
// Validate if the requested user is allowed
|
||||
if ($subordinates->contains('id', $requestedUserId)) {
|
||||
return $requestedUserId;
|
||||
}
|
||||
|
||||
// If invalid ID or not authorized, return default
|
||||
return $defaultUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show user's assigned assets with optional manager view functionality.
|
||||
*
|
||||
*/
|
||||
public function getIndex() : View | RedirectResponse
|
||||
public function getIndex(Request $request) : View | RedirectResponse
|
||||
{
|
||||
$user = User::with(
|
||||
$authUser = auth()->user();
|
||||
$settings = Setting::getSettings();
|
||||
$subordinates = collect();
|
||||
$selectedUserId = $authUser->id;
|
||||
|
||||
// Process manager view if enabled
|
||||
if ($settings->manager_view_enabled) {
|
||||
$subordinates = $this->getViewableUsers($authUser);
|
||||
$selectedUserId = $this->getSelectedUserId($request, $subordinates, $authUser->id);
|
||||
}
|
||||
|
||||
// Load the data for the user to be viewed (either auth user or selected subordinate)
|
||||
$userToView = User::with([
|
||||
'assets',
|
||||
'assets.model',
|
||||
'assets.model.fieldset.fields',
|
||||
'consumables',
|
||||
'accessories',
|
||||
'licenses',
|
||||
)->find(auth()->id());
|
||||
|
||||
$field_array = array();
|
||||
|
||||
// Loop through all the custom fields that are applied to any model the user has assigned
|
||||
foreach ($user->assets as $asset) {
|
||||
|
||||
// Make sure the model has a custom fieldset before trying to loop through the associated fields
|
||||
if ($asset->model->fieldset) {
|
||||
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
// check and make sure they're allowed to see the value of the custom field
|
||||
if ($field->display_in_user_view == '1') {
|
||||
$field_array[$field->db_column] = $field->name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
'licenses'
|
||||
])->find($selectedUserId);
|
||||
|
||||
// If the user to view couldn't be found (shouldn't happen with proper logic), redirect with error
|
||||
if (!$userToView) {
|
||||
return redirect()->route('view-assets')->with('error', trans('admin/users/message.user_not_found'));
|
||||
}
|
||||
|
||||
// Since some models may re-use the same fieldsets/fields, let's make the array unique so we don't repeat columns
|
||||
array_unique($field_array);
|
||||
// Process custom fields for the user being viewed
|
||||
$fieldArray = $this->extractCustomFields($userToView);
|
||||
|
||||
if (isset($user->id)) {
|
||||
return view('account/view-assets', compact('user', 'field_array' ))
|
||||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', $user->id));
|
||||
// Pass the necessary data to the view
|
||||
return view('account/view-assets', [
|
||||
'user' => $userToView, // Use 'user' for compatibility with the existing view
|
||||
'field_array' => $fieldArray,
|
||||
'settings' => $settings,
|
||||
'subordinates' => $subordinates,
|
||||
'selectedUserId' => $selectedUserId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,6 +73,7 @@ class Kernel extends HttpKernel
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'api-throttle' => \App\Http\Middleware\SetAPIResponseHeaders::class,
|
||||
'health' => null,
|
||||
];
|
||||
}
|
||||
|
||||
82
app/Http/Middleware/SetAPIResponseHeaders.php
Normal file
82
app/Http/Middleware/SetAPIResponseHeaders.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SetAPIResponseHeaders extends ThrottleRequests
|
||||
{
|
||||
|
||||
/**
|
||||
* Add the rate limit headers to the response.
|
||||
*
|
||||
* This extends the original ThrottleRequests middleware to add the 'X-RateLimit-Reset' and 'Retry-After' headers, even
|
||||
* if the rate limit is not exceeded.
|
||||
* @param $maxAttempts
|
||||
* @param $remainingAttempts
|
||||
* @param $retryAfter
|
||||
* @param Response|null $response
|
||||
* @return array|int[]
|
||||
*/
|
||||
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null, ?Response $response = null)
|
||||
{
|
||||
if ($response &&
|
||||
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
|
||||
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
|
||||
$headers = [];
|
||||
$headers['Retry-After'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
|
||||
return $headers;
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'X-RateLimit-Limit' => $maxAttempts,
|
||||
'X-RateLimit-Remaining' => $remainingAttempts,
|
||||
];
|
||||
|
||||
if (! is_null($retryAfter)) {
|
||||
$headers['Retry-After'] = $retryAfter;
|
||||
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
|
||||
$this->limiter->hit($limit->key, $limit->decaySeconds);
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts),
|
||||
$this->getTimeUntilNextRetry($limit->key) // this is the only line we changed
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,7 @@ class SettingsSamlRequest extends FormRequest
|
||||
public function withValidator($validator)
|
||||
{
|
||||
$validator->after(function ($validator) {
|
||||
$setting = Setting::getSettings();
|
||||
if ($this->input('saml_enabled') == '1') {
|
||||
$idpMetadata = $this->input('saml_idp_metadata');
|
||||
if (! empty($idpMetadata)) {
|
||||
@@ -56,7 +57,7 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
|
||||
$was_custom_x509cert = strpos($setting->saml_custom_settings, 'sp_x509cert') !== false;
|
||||
|
||||
$custom_x509cert = '';
|
||||
$custom_privateKey = '';
|
||||
@@ -126,10 +127,14 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
|
||||
if (! (empty($x509cert) && empty($privateKey))) {
|
||||
$this->merge([
|
||||
'saml_sp_x509cert' => $x509cert,
|
||||
'saml_sp_privatekey' => $privateKey,
|
||||
]);
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509cert' => $x509cert,
|
||||
// 'saml_sp_privatekey' => $privateKey,
|
||||
// ]);
|
||||
$setting->saml_sp_x509cert = $x509cert;
|
||||
$setting->saml_sp_privatekey = $privateKey;
|
||||
$setting->save();
|
||||
|
||||
}
|
||||
} else {
|
||||
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
|
||||
@@ -145,15 +150,21 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
|
||||
if (! empty($x509certNew)) {
|
||||
$this->merge([
|
||||
'saml_sp_x509certNew' => $x509certNew,
|
||||
]);
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509certNew' => $x509certNew,
|
||||
// ]);
|
||||
$setting->saml_sp_x509certNew = $x509certNew;
|
||||
$setting->save();
|
||||
}
|
||||
} else {
|
||||
$this->merge([
|
||||
'saml_sp_x509certNew' => '',
|
||||
]);
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509certNew' => '',
|
||||
// ]);
|
||||
$setting->saml_sp_x509certNew = '';
|
||||
$setting->save();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class StoreAssetModelRequest extends ImageUploadRequest
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
parent::prepareForValidation();
|
||||
|
||||
if ($this->category_id) {
|
||||
if ($category = Category::find($this->category_id)) {
|
||||
|
||||
@@ -39,7 +39,6 @@ class StoreAssetRequest extends ImageUploadRequest
|
||||
$this->merge([
|
||||
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
|
||||
'company_id' => $idForCurrentUser,
|
||||
'assigned_to' => $assigned_to ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ class StoreLabelSettings extends FormRequest
|
||||
|
||||
return [
|
||||
'labels_per_page' => 'numeric',
|
||||
'labels_width' => 'numeric',
|
||||
'labels_height' => 'numeric',
|
||||
'labels_width' => 'numeric|min:0.1',
|
||||
'labels_height' => 'numeric|min:0.1',
|
||||
'labels_pmargin_left' => 'numeric|nullable',
|
||||
'labels_pmargin_right' => 'numeric|nullable',
|
||||
'labels_pmargin_top' => 'numeric|nullable',
|
||||
|
||||
@@ -27,8 +27,6 @@ class StoreLdapSettings extends FormRequest
|
||||
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
|
||||
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
|
||||
'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://',
|
||||
'ldap_uname' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_pword' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_basedn' => 'nullable|required_if:ldap_enabled,1',
|
||||
'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1',
|
||||
'custom_forgot_pass_url' => 'nullable|url',
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Requests;
|
||||
use App\Models\Accessory;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreNotificationSettings extends FormRequest
|
||||
{
|
||||
@@ -26,6 +27,9 @@ class StoreNotificationSettings extends FormRequest
|
||||
return [
|
||||
'alert_email' => 'email_array|nullable',
|
||||
'admin_cc_email' => 'email_array|nullable',
|
||||
'admin_cc_always' => [
|
||||
Rule::in('0', '1'),
|
||||
],
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable|gt:0',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Traits\ConvertsBase64ToFiles;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \App\Helpers\Helper;
|
||||
|
||||
class UploadFileRequest extends Request
|
||||
{
|
||||
@@ -27,44 +28,76 @@ class UploadFileRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
$max_file_size = Helper::file_upload_max_size();
|
||||
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
|
||||
'file.*' => 'required|mimes:'.config('filesystems.allowed_upload_extensions_for_validator').'|max:'.$max_file_size,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes (if needed) and Saves a file to the appropriate location
|
||||
* Returns the 'short' (storage-relative) filename
|
||||
*
|
||||
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
|
||||
* a way to merge them or extend one into the other?
|
||||
*/
|
||||
public function handleFile(string $dirname, string $name_prefix, $file): string
|
||||
{
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
|
||||
|
||||
// Check for SVG and sanitize it
|
||||
if ($file->getMimeType() === 'image/svg+xml') {
|
||||
Log::debug('This is an SVG');
|
||||
Log::debug($file_name);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug('Upload no workie :( ');
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
$uploaded_file = $this->handleSVG($file);
|
||||
} else {
|
||||
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
|
||||
$uploaded_file = file_get_contents($file);
|
||||
}
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $uploaded_file);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
return $file_name;
|
||||
}
|
||||
}
|
||||
|
||||
public function handleSVG($file)
|
||||
{
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
return $sanitizer->sanitize($dirtySVG);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the validation error messages that apply to the request, but
|
||||
* replace the attribute name with the name of the file that was attempted and failed
|
||||
* to make it clearer to the user which file is the bad one.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
if (($this->file) && (is_array($this->file))) {
|
||||
|
||||
for ($i = 0; $i < count($this->file); $i++) {
|
||||
|
||||
try {
|
||||
|
||||
if ($this->file[$i]) {
|
||||
$attributes['file.'.$i] = $this->file[$i]->getClientOriginalName();
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$attributes['file.'.$i] = 'Invalid file';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class AccessoriesTransformer
|
||||
'checkout' => Gate::allows('checkout', Accessory::class),
|
||||
'checkin' => false,
|
||||
'update' => Gate::allows('update', Accessory::class),
|
||||
'delete' => Gate::allows('delete', Accessory::class),
|
||||
'delete' => $accessory->checkouts_count === 0 && Gate::allows('delete', Accessory::class),
|
||||
'clone' => Gate::allows('create', Accessory::class),
|
||||
|
||||
];
|
||||
@@ -94,6 +94,10 @@ class AccessoriesTransformer
|
||||
|
||||
public function transformAssignedTo($accessoryCheckout)
|
||||
{
|
||||
if (is_null($accessoryCheckout->assigned)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($accessoryCheckout->checkedOutToUser()) {
|
||||
return (new UsersTransformer)->transformUserCompact($accessoryCheckout->assigned);
|
||||
} elseif ($accessoryCheckout->checkedOutToLocation()) {
|
||||
|
||||
@@ -113,8 +113,8 @@ class ActionlogsTransformer
|
||||
|
||||
// Display the changes if the user is an admin or superadmin
|
||||
if (Gate::allows('admin')) {
|
||||
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old): '';
|
||||
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): '';
|
||||
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old, ['allowed_classes' => false]) : '';
|
||||
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new, ['allowed_classes' => false]) : '';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -198,8 +198,8 @@ class ActionlogsTransformer
|
||||
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
|
||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||
'remote_ip' => ($actionlog->remote_ip) ?? null,
|
||||
'user_agent' => ($actionlog->user_agent) ?? null,
|
||||
'remote_ip' => e($actionlog->remote_ip) ?? null,
|
||||
'user_agent' => e($actionlog->user_agent) ?? null,
|
||||
'action_source' => ($actionlog->action_source) ?? null,
|
||||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
@@ -59,7 +59,10 @@ class AssetMaintenancesTransformer
|
||||
'name'=> e($assetmaintenance->asset->defaultLoc->name),
|
||||
] : null,
|
||||
'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedownInline($assetmaintenance->notes) : null,
|
||||
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
|
||||
'supplier' => ($assetmaintenance->supplier) ? [
|
||||
'id' => $assetmaintenance->supplier->id,
|
||||
'name'=> e($assetmaintenance->supplier->name)
|
||||
] : null,
|
||||
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
||||
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
||||
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
|
||||
|
||||
@@ -105,6 +105,7 @@ class AssetModelsTransformer
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'filename' => e($file->filename),
|
||||
'note' => $file->note,
|
||||
'url' => route('show/modelfile', [$assetmodel->id, $file->id]),
|
||||
'created_by' => ($file->adminuser) ? [
|
||||
'id' => (int) $file->adminuser->id,
|
||||
|
||||
@@ -101,7 +101,7 @@ class AssetsTransformer
|
||||
'checkout_counter' => (int) $asset->checkout_counter,
|
||||
'requests_counter' => (int) $asset->requests_counter,
|
||||
'user_can_checkout' => (bool) $asset->availableForCheckout(),
|
||||
'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()),
|
||||
'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
|
||||
];
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ class AssetsTransformer
|
||||
'clone' => Gate::allows('create', Asset::class) ? true : false,
|
||||
'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false,
|
||||
'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false,
|
||||
'audit' => Gate::allows('audit', Asset::class) ? true : false,
|
||||
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class) && ($asset->deleted_at == '')) ? true : false,
|
||||
];
|
||||
|
||||
@@ -202,6 +203,7 @@ class AssetsTransformer
|
||||
'last_name'=> ($asset->assigned->last_name) ? e($asset->assigned->last_name) : null,
|
||||
'email'=> ($asset->assigned->email) ? e($asset->assigned->email) : null,
|
||||
'employee_number' => ($asset->assigned->employee_num) ? e($asset->assigned->employee_num) : null,
|
||||
'jobtitle' => $asset->assigned->jobtitle ? e($asset->assigned->jobtitle) : null,
|
||||
'type' => 'user',
|
||||
] : null;
|
||||
}
|
||||
@@ -302,30 +304,32 @@ class AssetsTransformer
|
||||
|
||||
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
|
||||
{
|
||||
if ($accessory_checkout->accessory) {
|
||||
$array = [
|
||||
'id' => $accessory_checkout->id,
|
||||
'accessory' => [
|
||||
'id' => $accessory_checkout->accessory->id,
|
||||
'name' => $accessory_checkout->accessory->name,
|
||||
],
|
||||
'assigned_to' => $accessory_checkout->assigned_to,
|
||||
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/' . e($accessory_checkout->accessory->image)) : null,
|
||||
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
|
||||
'created_by' => $accessory_checkout->adminuser ? [
|
||||
'id' => (int)$accessory_checkout->adminuser->id,
|
||||
'name' => e($accessory_checkout->adminuser->present()->fullName),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($accessory_checkout->deleted_at, 'datetime'),
|
||||
];
|
||||
|
||||
$array = [
|
||||
'id' => $accessory_checkout->id,
|
||||
'accessory' => [
|
||||
'id' => $accessory_checkout->accessory->id,
|
||||
'name' => $accessory_checkout->accessory->name,
|
||||
],
|
||||
'assigned_to' => $accessory_checkout->assigned_to,
|
||||
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
|
||||
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
|
||||
'created_by' => $accessory_checkout->adminuser ? [
|
||||
'id' => (int) $accessory_checkout->adminuser->id,
|
||||
'name'=> e($accessory_checkout->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
|
||||
];
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => false,
|
||||
'checkin' => Gate::allows('checkin', Accessory::class),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => false,
|
||||
'checkin' => Gate::allows('checkin', Accessory::class),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ namespace App\Http\Transformers;
|
||||
|
||||
class DatatablesTransformer
|
||||
{
|
||||
|
||||
/**
|
||||
* Transform data for bootstrap tables and API responses for lists of things
|
||||
**/
|
||||
public function transformDatatables($objects, $total = null)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
@@ -11,4 +15,15 @@ class DatatablesTransformer
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform data for returning the status of items within a bulk action
|
||||
**/
|
||||
public function transformBulkResponseWithStatusAndObjects($objects, $total)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
$objects_array['rows'] = $objects;
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,11 @@ class DepreciationsTransformer
|
||||
$array = [
|
||||
'id' => (int) $depreciation->id,
|
||||
'name' => e($depreciation->name),
|
||||
'months' => $depreciation->months.' '.trans('general.months'),
|
||||
'months' => trans_choice('general.months_plural', $depreciation->months),
|
||||
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
|
||||
'assets_count' => $depreciation->assets_count,
|
||||
'models_count' => $depreciation->models_count,
|
||||
'licenses_count' => $depreciation->licenses_count,
|
||||
'assets_count' => ($depreciation->assets_count > 0) ? (int) $depreciation->assets_count : 0,
|
||||
'models_count' => ($depreciation->models_count > 0) ? (int) $depreciation->models_count : 0,
|
||||
'licenses_count' => ($depreciation->licenses_count > 0) ? (int) $depreciation->licenses_count : 0,
|
||||
'created_by' => ($depreciation->adminuser) ? [
|
||||
'id' => (int) $depreciation->adminuser->id,
|
||||
'name'=> e($depreciation->adminuser->present()->fullName()),
|
||||
|
||||
@@ -24,7 +24,7 @@ class GroupsTransformer
|
||||
$array = [
|
||||
'id' => (int) $group->id,
|
||||
'name' => e($group->name),
|
||||
'permissions' => json_decode($group->permissions),
|
||||
'permissions' => $group->decodePermissions(),
|
||||
'users_count' => (int) $group->users_count,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($group->notes),
|
||||
'created_by' => ($group->adminuser) ? [
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
@@ -12,20 +13,20 @@ class LicenseSeatsTransformer
|
||||
public function transformLicenseSeats(Collection $seats, $total)
|
||||
{
|
||||
$array = [];
|
||||
$seat_count = 0;
|
||||
|
||||
foreach ($seats as $seat) {
|
||||
$seat_count++;
|
||||
$array[] = self::transformLicenseSeat($seat, $seat_count);
|
||||
$array[] = self::transformLicenseSeat($seat);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformLicenseSeat(LicenseSeat $seat, $seat_count = 0)
|
||||
public function transformLicenseSeat(LicenseSeat $seat)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $seat->id,
|
||||
'license_id' => (int) $seat->license->id,
|
||||
'updated_at' => Helper::getFormattedDateObject($seat->updated_at, 'datetime'), // we use updated_at here because the record gets updated when it's checked in or out
|
||||
'assigned_user' => ($seat->user) ? [
|
||||
'id' => (int) $seat->user->id,
|
||||
'name'=> e($seat->user->present()->fullName),
|
||||
@@ -36,24 +37,23 @@ class LicenseSeatsTransformer
|
||||
'name' => e($seat->user->department->name),
|
||||
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
|
||||
] : null,
|
||||
'assigned_asset' => ($seat->asset) ? [
|
||||
'id' => (int) $seat->asset->id,
|
||||
'name'=> e($seat->asset->present()->fullName),
|
||||
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
|
||||
] : null,
|
||||
'location' => ($seat->location()) ? [
|
||||
'id' => (int) $seat->location()->id,
|
||||
'name'=> e($seat->location()->name),
|
||||
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
|
||||
] : null,
|
||||
'reassignable' => (bool) $seat->license->reassignable,
|
||||
'notes' => e($seat->notes),
|
||||
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
|
||||
];
|
||||
|
||||
if ($seat_count != 0) {
|
||||
$array['name'] = trans('admin/licenses/general.seat_count', ['count' => $seat_count]);
|
||||
}
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => Gate::allows('checkout', License::class),
|
||||
'checkin' => Gate::allows('checkin', License::class),
|
||||
|
||||
@@ -57,6 +57,10 @@ class LocationsTransformer
|
||||
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($location->notes),
|
||||
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
|
||||
'created_by' => $location->adminuser ? [
|
||||
'id' => (int) $location->adminuser->id,
|
||||
'name'=> e($location->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
|
||||
'parent' => ($location->parent) ? [
|
||||
'id' => (int) $location->parent->id,
|
||||
|
||||
43
app/Http/Transformers/ProfileTransformer.php
Normal file
43
app/Http/Transformers/ProfileTransformer.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ProfileTransformer
|
||||
{
|
||||
public function transformFiles(Collection $files, $total)
|
||||
{
|
||||
$array = [];
|
||||
foreach ($files as $file) {
|
||||
$array[] = self::transformFile($file);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformFile(Actionlog $file)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'icon' => Helper::filetype_icon($file->filename),
|
||||
'item' => ($file->item) ? [
|
||||
'name' => ($file->itemType()=='user') ? e($file->item->getFullNameAttribute()) : e($file->item->getDisplayNameAttribute()),
|
||||
'type' => e($file->itemType()),
|
||||
] : null,
|
||||
'filename' => e($file->filename),
|
||||
'signature_file' => ($file->accept_signature) ? route('profile.signature.view', ['filename' => $file->accept_signature ]) : null,
|
||||
'note' => e($file->note),
|
||||
'url' => route('profile.storedeula.download', ['filename' => $file->filename]),
|
||||
'file' => route('profile.storedeula.download', ['filename' => $file->filename]),
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,6 +45,10 @@ class SuppliersTransformer
|
||||
'components_count' => (int) $supplier->components_count,
|
||||
'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedownInline($supplier->notes) : null,
|
||||
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
|
||||
'created_by' => $supplier->adminuser ? [
|
||||
'id' => (int) $supplier->adminuser->id,
|
||||
'name'=> e($supplier->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
|
||||
|
||||
];
|
||||
|
||||
60
app/Http/Transformers/UploadedFilesTransformer.php
Normal file
60
app/Http/Transformers/UploadedFilesTransformer.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Actionlog;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UploadedFilesTransformer
|
||||
{
|
||||
public function transformFiles(Collection $files, $total)
|
||||
{
|
||||
$array = [];
|
||||
foreach ($files as $file) {
|
||||
$array[] = self::transformFile($file);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformFile(Actionlog $file)
|
||||
{
|
||||
$snipeModel = $file->item_type;
|
||||
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'icon' => Helper::filetype_icon($file->filename),
|
||||
'name' => e($file->filename),
|
||||
'item' => ($file->item_type) ? [
|
||||
'id' => (int) $file->item_id,
|
||||
'type' => strtolower(class_basename($file->item_type)),
|
||||
] : null,
|
||||
'filename' => e($file->filename),
|
||||
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
|
||||
'url' => $file->uploads_file_url(),
|
||||
'note' => ($file->note) ? e($file->note) : null,
|
||||
'created_by' => ($file->adminuser) ? [
|
||||
'id' => (int) $file->adminuser->id,
|
||||
'name'=> e($file->adminuser->present()->fullName),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
|
||||
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
|
||||
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'delete' => (Gate::allows('update', $snipeModel) && ($file->deleted_at == '')),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user