Compare commits
1029 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af06e42056 | ||
|
|
9a2440dc4b | ||
|
|
2ac1c1636c | ||
|
|
beae8efb21 | ||
|
|
9839e5e566 | ||
|
|
d14ab7e3e1 | ||
|
|
e7f74d94c1 | ||
|
|
e97cf011b6 | ||
|
|
ed23505054 | ||
|
|
001e721530 | ||
|
|
8210da6e82 | ||
|
|
f88683766b | ||
|
|
e4385c0f8c | ||
|
|
0550fe0ffa | ||
|
|
7fb3a9b82c | ||
|
|
ecb1e87fe6 | ||
|
|
f43df5f041 | ||
|
|
95cc48e422 | ||
|
|
9a2ed804ca | ||
|
|
d20fad28e5 | ||
|
|
ae813ddf75 | ||
|
|
bb42109c0c | ||
|
|
f46ecf8ec0 | ||
|
|
b9e821c0e6 | ||
|
|
9ee28c7513 | ||
|
|
1a8ba06702 | ||
|
|
0fd232e70d | ||
|
|
ee4d69b1c5 | ||
|
|
d1ad111949 | ||
|
|
31c5350941 | ||
|
|
7eb70e17e0 | ||
|
|
3dfcb46991 | ||
|
|
96eb96f964 | ||
|
|
a2f08bd3ba | ||
|
|
e009fbe59f | ||
|
|
5bb4f271aa | ||
|
|
154db9a416 | ||
|
|
cf9d0201e0 | ||
|
|
7ebd21bc04 | ||
|
|
5707df0239 | ||
|
|
197a84be94 | ||
|
|
b4fa4c77d7 | ||
|
|
cfec142c3b | ||
|
|
48dfc699d7 | ||
|
|
ec723a3da1 | ||
|
|
f8a72db696 | ||
|
|
83ee64f155 | ||
|
|
b7d12ff944 | ||
|
|
0858fec7f1 | ||
|
|
206bd675f2 | ||
|
|
92695782ff | ||
|
|
c447e4d29b | ||
|
|
811f89b1de | ||
|
|
be3e572440 | ||
|
|
824ebc19c0 | ||
|
|
a0f7fdc57a | ||
|
|
450c1b9d56 | ||
|
|
79232fc434 | ||
|
|
0b3f511534 | ||
|
|
7f18983a49 | ||
|
|
b7d9790acb | ||
|
|
1a5785a8d3 | ||
|
|
320d660e83 | ||
|
|
fb903b2fda | ||
|
|
c18646d096 | ||
|
|
7bf398aca4 | ||
|
|
f6bb655383 | ||
|
|
19f71face9 | ||
|
|
d82b94e281 | ||
|
|
893944403e | ||
|
|
0d3c18d1df | ||
|
|
d7873f257d | ||
|
|
7e3f718797 | ||
|
|
be79a1f3d6 | ||
|
|
a8032ac388 | ||
|
|
21d8225696 | ||
|
|
766c2b22cb | ||
|
|
db79f92423 | ||
|
|
bdddab5b8b | ||
|
|
031adc3be4 | ||
|
|
e7c1418314 | ||
|
|
c906026acd | ||
|
|
56c2740b68 | ||
|
|
4688d62b9f | ||
|
|
99686bd73a | ||
|
|
120e224961 | ||
|
|
e27d69a31d | ||
|
|
c492ba7245 | ||
|
|
53658e365f | ||
|
|
4cfa0e36b1 | ||
|
|
9d9b5d3885 | ||
|
|
36f9905be0 | ||
|
|
a815e0ab8c | ||
|
|
6bfec08a8c | ||
|
|
2d2cd68061 | ||
|
|
fd642e95eb | ||
|
|
9ab3370be5 | ||
|
|
4dcc1ffdbc | ||
|
|
7d466f3584 | ||
|
|
7718abaa72 | ||
|
|
59c5a1ea87 | ||
|
|
0cf70c9e16 | ||
|
|
6174f9b93f | ||
|
|
c3d2e8ff26 | ||
|
|
192f703885 | ||
|
|
be93b23488 | ||
|
|
b079d0d6d5 | ||
|
|
c6c75cc11f | ||
|
|
b188285bc9 | ||
|
|
6d659a84b8 | ||
|
|
3e3828229d | ||
|
|
bf6a0f8d2f | ||
|
|
3873c4b253 | ||
|
|
6cc23f69f9 | ||
|
|
a467a6999e | ||
|
|
e0eb10ca1e | ||
|
|
99c4c73c09 | ||
|
|
bde45cbb34 | ||
|
|
c408c27bf4 | ||
|
|
b14f37d966 | ||
|
|
bfa9c0c528 | ||
|
|
9cc9cddd68 | ||
|
|
fe2261c88d | ||
|
|
6aeb3c0a47 | ||
|
|
dfaa1c9578 | ||
|
|
0ef1dfe061 | ||
|
|
ba8bcd6413 | ||
|
|
7854003ec2 | ||
|
|
c71dd9b68a | ||
|
|
dfeabbc85d | ||
|
|
b8b9ac8a1b | ||
|
|
80ac2607cd | ||
|
|
3552fb1fd8 | ||
|
|
54a96b8453 | ||
|
|
03be4e74df | ||
|
|
e9ddd1af81 | ||
|
|
f305885e8e | ||
|
|
f0b9cd7820 | ||
|
|
59accca89d | ||
|
|
e72ebfb94b | ||
|
|
0b7316d548 | ||
|
|
d0cf76989a | ||
|
|
90a2bf7c9c | ||
|
|
95945412b1 | ||
|
|
c299efca0c | ||
|
|
5e4918579a | ||
|
|
db75f0e894 | ||
|
|
5a6c13e364 | ||
|
|
4b22f07dd7 | ||
|
|
57cb5146fc | ||
|
|
07708f530e | ||
|
|
5c68353e62 | ||
|
|
53728e5c71 | ||
|
|
b965d170ab | ||
|
|
34a1bb7152 | ||
|
|
8787f228d9 | ||
|
|
03cde9a72c | ||
|
|
623655b6f6 | ||
|
|
da6830225a | ||
|
|
a729410fe8 | ||
|
|
bba4036e53 | ||
|
|
39c71c6027 | ||
|
|
03a9219a7c | ||
|
|
a8f6bbd86a | ||
|
|
9a2ee2638b | ||
|
|
2a813244a2 | ||
|
|
b50894fca1 | ||
|
|
41fa2d1aa1 | ||
|
|
54d39c04ad | ||
|
|
3c1365b2c8 | ||
|
|
5858c90e71 | ||
|
|
f0ef06ebe1 | ||
|
|
700f7de748 | ||
|
|
af2ea7ac03 | ||
|
|
690d8255c9 | ||
|
|
aded2193a2 | ||
|
|
6d99b2a68c | ||
|
|
55a619778f | ||
|
|
6066c249d5 | ||
|
|
025ea93f05 | ||
|
|
54fd8f81ff | ||
|
|
ca43554327 | ||
|
|
61bdb88ba5 | ||
|
|
36696ab56e | ||
|
|
f0f9b93652 | ||
|
|
a2fae76eaf | ||
|
|
8b2f8ef3cb | ||
|
|
5307e57bd9 | ||
|
|
15518852aa | ||
|
|
60fc1d3f6d | ||
|
|
d1a8d76d85 | ||
|
|
803f5ad0ab | ||
|
|
0e0fe967e4 | ||
|
|
192917cc84 | ||
|
|
81880645ed | ||
|
|
9eb4b0dda7 | ||
|
|
2f0ed129f0 | ||
|
|
3361b859c0 | ||
|
|
e27a9b137b | ||
|
|
89e2a3ae3c | ||
|
|
5f85d8132b | ||
|
|
ca1285ec08 | ||
|
|
75bf8f3d58 | ||
|
|
324da7c0c8 | ||
|
|
779fc6d195 | ||
|
|
db59106c3e | ||
|
|
88fb1370f0 | ||
|
|
943cf40247 | ||
|
|
ff57f10e9f | ||
|
|
91bb76fd8a | ||
|
|
893454dca7 | ||
|
|
de0b5a6149 | ||
|
|
8fd4e35244 | ||
|
|
e71e57f16a | ||
|
|
3f5840d390 | ||
|
|
d3f4205f09 | ||
|
|
5b946087c4 | ||
|
|
ff8d98c97c | ||
|
|
2fbbe430b5 | ||
|
|
f0af750b0a | ||
|
|
88cf456386 | ||
|
|
d8049209ca | ||
|
|
dd40ddf5a5 | ||
|
|
a73fd24695 | ||
|
|
70c8ad9797 | ||
|
|
0290257734 | ||
|
|
4fe689dc5d | ||
|
|
0769f585ea | ||
|
|
04562e6d4a | ||
|
|
22d2ad9248 | ||
|
|
6deb26fafe | ||
|
|
6c1de7ff05 | ||
|
|
7f5f4a1297 | ||
|
|
c68c0e1208 | ||
|
|
c256536d21 | ||
|
|
4159a0effa | ||
|
|
b8f7cd81eb | ||
|
|
b381528668 | ||
|
|
6d66d7e215 | ||
|
|
b5bf8e9a37 | ||
|
|
ba197c8857 | ||
|
|
124b249df4 | ||
|
|
2a6919c438 | ||
|
|
8b4a9aa382 | ||
|
|
99cd552d5c | ||
|
|
6c7e5cb9cf | ||
|
|
6ebb01a081 | ||
|
|
5591c861b9 | ||
|
|
d37280567d | ||
|
|
e7b0ee2539 | ||
|
|
c593b3645c | ||
|
|
28ae90fa8a | ||
|
|
c7be25078e | ||
|
|
3dc2cc9f22 | ||
|
|
ab86e42b2e | ||
|
|
9af9ed9eb9 | ||
|
|
250a797339 | ||
|
|
a0f3fc6d76 | ||
|
|
74e647fea7 | ||
|
|
55ee90b25d | ||
|
|
eec445fcf5 | ||
|
|
cef22c3158 | ||
|
|
61fb38087e | ||
|
|
0e93495ca2 | ||
|
|
444e250609 | ||
|
|
77a6f6f400 | ||
|
|
15bfd07f30 | ||
|
|
fecf8015a1 | ||
|
|
79ab0d8dc2 | ||
|
|
b4b6d6b571 | ||
|
|
8c73a47afb | ||
|
|
f82ffe378c | ||
|
|
984c2a8fd4 | ||
|
|
6736b1c4e7 | ||
|
|
d409be6d43 | ||
|
|
e1b33f3087 | ||
|
|
740d5a6846 | ||
|
|
d19df4ded8 | ||
|
|
03a4512406 | ||
|
|
de992e4df3 | ||
|
|
a85251aa83 | ||
|
|
26a1181765 | ||
|
|
cef030cf55 | ||
|
|
2bfa05fd2d | ||
|
|
30904dd019 | ||
|
|
1d0d25db37 | ||
|
|
cbff66c9db | ||
|
|
27231d49ea | ||
|
|
765417c0be | ||
|
|
49a255c8fb | ||
|
|
925d3a23c6 | ||
|
|
6966c132d0 | ||
|
|
c29ab90029 | ||
|
|
d2bbc09892 | ||
|
|
74a2c29bc2 | ||
|
|
2c64739e8f | ||
|
|
37f950ab42 | ||
|
|
b07a254e60 | ||
|
|
087cdd859e | ||
|
|
829d44bd27 | ||
|
|
db76090e10 | ||
|
|
03cf3b5431 | ||
|
|
40f101d471 | ||
|
|
7aa6ef5f6c | ||
|
|
186b94751d | ||
|
|
6f2717a876 | ||
|
|
a014af4c47 | ||
|
|
2dd31544fe | ||
|
|
237acdcff0 | ||
|
|
5db7a7c196 | ||
|
|
68acdff11b | ||
|
|
58e72e5ee6 | ||
|
|
6b43cd10ba | ||
|
|
b2c9a38db8 | ||
|
|
6dcdb5abae | ||
|
|
56576d9e45 | ||
|
|
d5c3ee5ed0 | ||
|
|
18db0a50f1 | ||
|
|
d596ced0a0 | ||
|
|
78fb2b2239 | ||
|
|
1472e9d5b5 | ||
|
|
fcbc7e4540 | ||
|
|
93bf541ce7 | ||
|
|
0e48d7b080 | ||
|
|
1e6c85da41 | ||
|
|
c5a23e8f5e | ||
|
|
b6d2392303 | ||
|
|
9995f1a743 | ||
|
|
d6f251e992 | ||
|
|
4be95eac4b | ||
|
|
8914d14681 | ||
|
|
d4725b61be | ||
|
|
aa0b627fe7 | ||
|
|
5be5e3271d | ||
|
|
dd5d5cc07c | ||
|
|
84c3709161 | ||
|
|
96e2d74ae3 | ||
|
|
bf93e8cc32 | ||
|
|
d1a8955ef9 | ||
|
|
149ac4bdf8 | ||
|
|
2d036c64e9 | ||
|
|
8db2470ac4 | ||
|
|
79156ff8f4 | ||
|
|
8e86d780bf | ||
|
|
f6ef139111 | ||
|
|
12ec2d1f7a | ||
|
|
0dfc28b0e8 | ||
|
|
b3132a4a8f | ||
|
|
6cd25fbdeb | ||
|
|
4be8ba9f17 | ||
|
|
df8008f1ed | ||
|
|
77547c528b | ||
|
|
bfb910f375 | ||
|
|
57e80ee317 | ||
|
|
de1189295a | ||
|
|
20d0dce73e | ||
|
|
144a32b1ca | ||
|
|
8244a2ad23 | ||
|
|
77c3b8f8c1 | ||
|
|
7b34cf1a31 | ||
|
|
21ec670531 | ||
|
|
b2eacb147b | ||
|
|
729e3eb70d | ||
|
|
978533b2f4 | ||
|
|
0358d13ddb | ||
|
|
c944304444 | ||
|
|
096393389c | ||
|
|
9eb7b668d1 | ||
|
|
6728089106 | ||
|
|
33b59d7bed | ||
|
|
888bdbdb68 | ||
|
|
d67c931f6a | ||
|
|
dbdc511eff | ||
|
|
f47b960566 | ||
|
|
d016076806 | ||
|
|
23fa5d0bf4 | ||
|
|
486c708911 | ||
|
|
e5c2d77c7d | ||
|
|
ce16eae508 | ||
|
|
dc73dbfbfd | ||
|
|
dae26e0378 | ||
|
|
1bb1f7342f | ||
|
|
420e8bc85a | ||
|
|
a521523d45 | ||
|
|
25884a893e | ||
|
|
d1e9fbfa24 | ||
|
|
da015ec4a8 | ||
|
|
1451b4f45d | ||
|
|
b6da68a69c | ||
|
|
dee92cfc6c | ||
|
|
dec77890bd | ||
|
|
0e1289f12f | ||
|
|
7b33f95e83 | ||
|
|
ab6744dfba | ||
|
|
0fd940ffa4 | ||
|
|
5893e25b43 | ||
|
|
7c3bbe3097 | ||
|
|
858d382e26 | ||
|
|
de16fee00a | ||
|
|
7deab0f53b | ||
|
|
e59ec8b27f | ||
|
|
6d98bd6846 | ||
|
|
58768e5aee | ||
|
|
28a450ea25 | ||
|
|
1393f44070 | ||
|
|
8016939f31 | ||
|
|
c1ad2f9376 | ||
|
|
9575cd2651 | ||
|
|
cf086b711e | ||
|
|
53db96edad | ||
|
|
3b62c4a83a | ||
|
|
5f3147cf36 | ||
|
|
738896bdc2 | ||
|
|
e2834fab90 | ||
|
|
d687e1d762 | ||
|
|
6256abddf2 | ||
|
|
b26fbf986f | ||
|
|
5c9b1ed43a | ||
|
|
14eb6b387b | ||
|
|
35ebe33e4e | ||
|
|
9035707bd6 | ||
|
|
aa1e06f021 | ||
|
|
30b1cfabf5 | ||
|
|
e75d22ab73 | ||
|
|
b1e17743b8 | ||
|
|
e2c0f01a10 | ||
|
|
f88fee0f21 | ||
|
|
c0669150fb | ||
|
|
f3c12f38b6 | ||
|
|
5e19178a30 | ||
|
|
90cddb7aee | ||
|
|
6d828964be | ||
|
|
971fcf5800 | ||
|
|
2ad270cf33 | ||
|
|
8ce78c6b31 | ||
|
|
af3c8195af | ||
|
|
117b4c59cc | ||
|
|
194d0733d4 | ||
|
|
a371e8d53f | ||
|
|
8f09cca043 | ||
|
|
39bca49e8f | ||
|
|
b8269020ae | ||
|
|
601c129bbf | ||
|
|
b293d00699 | ||
|
|
75a0cf97e2 | ||
|
|
c055e3af21 | ||
|
|
a1f93e733c | ||
|
|
49073742b5 | ||
|
|
187206cb88 | ||
|
|
8420cb7ec1 | ||
|
|
75252bce05 | ||
|
|
794824713e | ||
|
|
8f6ea84fca | ||
|
|
ea1b792a93 | ||
|
|
4ffb8f14b8 | ||
|
|
d023f61bc4 | ||
|
|
dd5ca73602 | ||
|
|
2632f730d1 | ||
|
|
24c158bfe6 | ||
|
|
3d4a5a8066 | ||
|
|
db7e0b56f2 | ||
|
|
f2478d813c | ||
|
|
192aa9eb71 | ||
|
|
0eef0fc1dd | ||
|
|
81f8fe34cd | ||
|
|
f744696043 | ||
|
|
29b0780c6c | ||
|
|
6b3b673daa | ||
|
|
a4876e9f3e | ||
|
|
925258bfb4 | ||
|
|
1a10aa0dda | ||
|
|
d6f8d1b464 | ||
|
|
09a102fea8 | ||
|
|
304fce73fc | ||
|
|
295a68bb7a | ||
|
|
3aeb521782 | ||
|
|
f587d2248b | ||
|
|
8579c5a68a | ||
|
|
835b461d7d | ||
|
|
b8a37a0c73 | ||
|
|
41b5b1dfd0 | ||
|
|
c7596e7741 | ||
|
|
d4fa81301d | ||
|
|
ec7245965f | ||
|
|
de76e8db5f | ||
|
|
a52575c7bf | ||
|
|
bf6703c2e8 | ||
|
|
4db1dd8afc | ||
|
|
c39e3acb59 | ||
|
|
7a44da85a0 | ||
|
|
890b613f71 | ||
|
|
1014bd74e0 | ||
|
|
db385e024b | ||
|
|
c8bff3ef38 | ||
|
|
10bc35d604 | ||
|
|
eea65a3f26 | ||
|
|
3b21a19491 | ||
|
|
7a52477294 | ||
|
|
ef0bd72076 | ||
|
|
ff824ec4db | ||
|
|
75032def9e | ||
|
|
3a0f738fb0 | ||
|
|
55846cc717 | ||
|
|
1784278a59 | ||
|
|
afac0bc441 | ||
|
|
ffbee77f6f | ||
|
|
b69b5fdf84 | ||
|
|
89e06054bf | ||
|
|
3159e7713a | ||
|
|
adf6e7d1cd | ||
|
|
ba3662a9ed | ||
|
|
05ea61421f | ||
|
|
22ef2ce0b6 | ||
|
|
51d3d130e4 | ||
|
|
d8a8e1cc09 | ||
|
|
522dc1db2a | ||
|
|
db907815ff | ||
|
|
ae6abdddad | ||
|
|
63c9fbe10c | ||
|
|
101dfd01f2 | ||
|
|
5db5134ae0 | ||
|
|
5294489b0e | ||
|
|
05b2b8fb59 | ||
|
|
0100c56046 | ||
|
|
e81b221fd1 | ||
|
|
f374ac1bf7 | ||
|
|
524c6c502e | ||
|
|
614e858e44 | ||
|
|
708b1a962c | ||
|
|
4e55a18a60 | ||
|
|
3de1de9dc6 | ||
|
|
e320d2ba05 | ||
|
|
ed78a4b8a0 | ||
|
|
376eb52f00 | ||
|
|
8ecceeacda | ||
|
|
f4cfb31bf4 | ||
|
|
227dc7e81d | ||
|
|
152d985ebc | ||
|
|
ef1e8df001 | ||
|
|
5c2b1a3b70 | ||
|
|
66c3f5432d | ||
|
|
de413408f5 | ||
|
|
059126f642 | ||
|
|
3bc43210ab | ||
|
|
82194cef8a | ||
|
|
1956a16d1e | ||
|
|
e1c095adca | ||
|
|
45a2932f4b | ||
|
|
b6e3715cd8 | ||
|
|
aa9c0078a1 | ||
|
|
9677115055 | ||
|
|
d45e90e358 | ||
|
|
f692fb5bff | ||
|
|
83692696ba | ||
|
|
bbb15d610f | ||
|
|
7ebb7876c4 | ||
|
|
bd581d01a3 | ||
|
|
de2ebba577 | ||
|
|
351274c633 | ||
|
|
53fab3e9ba | ||
|
|
a0c0b7b1eb | ||
|
|
5a34d43a86 | ||
|
|
86a0f77e6f | ||
|
|
9d00ae6e50 | ||
|
|
8d23398176 | ||
|
|
5aae930e8c | ||
|
|
6db096c336 | ||
|
|
78d27e82c8 | ||
|
|
2a4fef6a61 | ||
|
|
9daeeeb851 | ||
|
|
92671823d8 | ||
|
|
19396b2107 | ||
|
|
f0332a7388 | ||
|
|
70cb5ed215 | ||
|
|
d309f67df0 | ||
|
|
7dc070ec2c | ||
|
|
2ecdf19569 | ||
|
|
c56f2625b7 | ||
|
|
3dc154991a | ||
|
|
a5f57c050f | ||
|
|
36c4cd98e0 | ||
|
|
f6ef6039f4 | ||
|
|
6867563e4a | ||
|
|
21820262ce | ||
|
|
bae1203b64 | ||
|
|
0f2ff7aba2 | ||
|
|
0ce90834f6 | ||
|
|
0e182bb2b9 | ||
|
|
7afff69fab | ||
|
|
136d59e4b9 | ||
|
|
6d9cbac928 | ||
|
|
efcd8d339b | ||
|
|
7e09ce468b | ||
|
|
baf714b41f | ||
|
|
b0808e846d | ||
|
|
0e3e10a707 | ||
|
|
cbf3d5d071 | ||
|
|
68d355a33d | ||
|
|
853923013a | ||
|
|
737061a983 | ||
|
|
0438d55284 | ||
|
|
924962c156 | ||
|
|
a8c42843f6 | ||
|
|
63c4b7b6b6 | ||
|
|
ff06387d90 | ||
|
|
fa46622bad | ||
|
|
fd65780287 | ||
|
|
6478ec86ac | ||
|
|
748e0eb44f | ||
|
|
507c07e9eb | ||
|
|
5185a9e590 | ||
|
|
1ade088a8f | ||
|
|
5c087181be | ||
|
|
08bb314bbc | ||
|
|
7ddb9becd4 | ||
|
|
7d96709ee2 | ||
|
|
4413a9f0b6 | ||
|
|
9611b55fb9 | ||
|
|
dffd535f68 | ||
|
|
4aaf2da651 | ||
|
|
80ac46af78 | ||
|
|
13c58b54fa | ||
|
|
355119187a | ||
|
|
0d96587b04 | ||
|
|
be806def91 | ||
|
|
8acf8027e9 | ||
|
|
893a1fec57 | ||
|
|
d182e40aef | ||
|
|
6118aca949 | ||
|
|
f0ac83179f | ||
|
|
8969ffc14b | ||
|
|
9fb130146a | ||
|
|
d285ff673a | ||
|
|
7f586da856 | ||
|
|
c35f5b3116 | ||
|
|
12927d2d54 | ||
|
|
6b06b87547 | ||
|
|
058f8ae3b8 | ||
|
|
1faedac04b | ||
|
|
666c7321af | ||
|
|
6cbaf7396a | ||
|
|
b706acaa9f | ||
|
|
5870acb193 | ||
|
|
c00b633312 | ||
|
|
1c0ee7c4c5 | ||
|
|
961741b809 | ||
|
|
5a66f0f373 | ||
|
|
5a1e1c73c9 | ||
|
|
3be68ec721 | ||
|
|
0f81e494ee | ||
|
|
d88f66b019 | ||
|
|
027edbdb21 | ||
|
|
cf03d25934 | ||
|
|
75232d2a70 | ||
|
|
bcb966af12 | ||
|
|
0272e58868 | ||
|
|
bcd988bb81 | ||
|
|
aa6c21f38d | ||
|
|
3fb5f6f5be | ||
|
|
5dc2ac9e22 | ||
|
|
0f85d6810b | ||
|
|
bf761946da | ||
|
|
d9fa2f0e91 | ||
|
|
8a25677a8d | ||
|
|
7c2da81700 | ||
|
|
a4799a495a | ||
|
|
b5de5ac19c | ||
|
|
638a7b2d91 | ||
|
|
dcf72ce2da | ||
|
|
8f6e0ad5be | ||
|
|
50e0b9b84e | ||
|
|
b6b93550fe | ||
|
|
baa3be728d | ||
|
|
240e642fe9 | ||
|
|
07a92d20d7 | ||
|
|
3f334406d1 | ||
|
|
dbd177576e | ||
|
|
0fb9f42ba4 | ||
|
|
b4542d4d42 | ||
|
|
4bfd7a7e4e | ||
|
|
3fe1562b92 | ||
|
|
27699aa99c | ||
|
|
48bbbe0f40 | ||
|
|
9dc226e3d6 | ||
|
|
98b20fc1cd | ||
|
|
980dccf31c | ||
|
|
bb2193d481 | ||
|
|
bf8fe316df | ||
|
|
96716626c6 | ||
|
|
e20338ff9e | ||
|
|
78530ae123 | ||
|
|
52d605d13e | ||
|
|
0182615e7e | ||
|
|
fad84d4437 | ||
|
|
c162e9a4de | ||
|
|
bf1e742df6 | ||
|
|
0e88a6b268 | ||
|
|
c1e870528e | ||
|
|
35fc001c58 | ||
|
|
339263a295 | ||
|
|
a44bd9abe0 | ||
|
|
b850d47282 | ||
|
|
4099c06b27 | ||
|
|
e559879f91 | ||
|
|
abb95e7872 | ||
|
|
869de3d251 | ||
|
|
f3526eccb9 | ||
|
|
880faa83a6 | ||
|
|
311f9fcefb | ||
|
|
8732f299e6 | ||
|
|
b30aac536a | ||
|
|
d7dc4ae0c0 | ||
|
|
5bb4c85ccb | ||
|
|
80dda198c5 | ||
|
|
9442736518 | ||
|
|
2fbad52c71 | ||
|
|
5975c9fac7 | ||
|
|
7b0e392ecd | ||
|
|
b51a10b46b | ||
|
|
6c58f59d72 | ||
|
|
cd9caa24ad | ||
|
|
cbb4b4d846 | ||
|
|
ea4cdadc6e | ||
|
|
6638d64d68 | ||
|
|
eb412c2bcb | ||
|
|
fde4a59510 | ||
|
|
1ee394aa69 | ||
|
|
707f90573c | ||
|
|
f8429ad357 | ||
|
|
aa5003d297 | ||
|
|
e9901f5e58 | ||
|
|
f0d04a4a57 | ||
|
|
32e3f748d8 | ||
|
|
fa465a84df | ||
|
|
82cf1a4467 | ||
|
|
3bbd49dbad | ||
|
|
ad21857cae | ||
|
|
2d18b73138 | ||
|
|
e7bc18dad4 | ||
|
|
62e4eabab0 | ||
|
|
d204eebab9 | ||
|
|
ed5823151b | ||
|
|
968d7d1f11 | ||
|
|
086683319a | ||
|
|
e5c1f4847d | ||
|
|
087e114d34 | ||
|
|
5a6b8bb856 | ||
|
|
102f567cb5 | ||
|
|
1a64879b65 | ||
|
|
4a0e5e4b88 | ||
|
|
01857fb056 | ||
|
|
3afe4938f9 | ||
|
|
9e0544e735 | ||
|
|
3993c6ad6b | ||
|
|
649563457d | ||
|
|
6ec75714f0 | ||
|
|
15916e6668 | ||
|
|
76d0562716 | ||
|
|
2ecc4aead7 | ||
|
|
d8210847a4 | ||
|
|
ece916e12f | ||
|
|
1a29d4f60f | ||
|
|
11832daf5c | ||
|
|
79555f5647 | ||
|
|
1868013704 | ||
|
|
39cc13f155 | ||
|
|
e636875797 | ||
|
|
260749337e | ||
|
|
20a3b556bb | ||
|
|
fa0c58e42a | ||
|
|
abbb94239d | ||
|
|
d89ef43834 | ||
|
|
f84ab2beda | ||
|
|
86d398abda | ||
|
|
883c65981b | ||
|
|
8eb96efa13 | ||
|
|
e9973670ea | ||
|
|
ef8d2d06df | ||
|
|
0b5bb520a7 | ||
|
|
233fb23cb8 | ||
|
|
fa89f45cb8 | ||
|
|
4c656c0321 | ||
|
|
87c6ee2035 | ||
|
|
aab190423f | ||
|
|
05e3e6bda6 | ||
|
|
1f299ed73e | ||
|
|
4ba9792fbe | ||
|
|
f405511b6b | ||
|
|
8ad5eb3e59 | ||
|
|
3df8fa99f0 | ||
|
|
cca97341e9 | ||
|
|
13195d06fd | ||
|
|
9b6e86b55c | ||
|
|
65cf7527b0 | ||
|
|
f74d50439c | ||
|
|
28e7ca5a84 | ||
|
|
8f64da5bc7 | ||
|
|
25f537e730 | ||
|
|
6d8af3d9c0 | ||
|
|
e56a46882d | ||
|
|
0476ffecdb | ||
|
|
47a0400a72 | ||
|
|
de3417d557 | ||
|
|
83b546c1c5 | ||
|
|
04709dc1df | ||
|
|
f48171dcab | ||
|
|
7b8362b64c | ||
|
|
188538651a | ||
|
|
a9fc7e04e9 | ||
|
|
4812285512 | ||
|
|
ec1fa8e90a | ||
|
|
3a1b432234 | ||
|
|
98f853128a | ||
|
|
276d2bc866 | ||
|
|
0472e3a3e5 | ||
|
|
a0afa9f2e8 | ||
|
|
0116fa9b95 | ||
|
|
42f0eebf8f | ||
|
|
8194660d16 | ||
|
|
532a9ef9fe | ||
|
|
0be69f57ac | ||
|
|
97f748d58e | ||
|
|
3662a58ad8 | ||
|
|
f5bf6a0beb | ||
|
|
0fc0aa39b1 | ||
|
|
c8cf46f62b | ||
|
|
28a1960fda | ||
|
|
44bb79c90e | ||
|
|
2f01bb2e63 | ||
|
|
71708e349c | ||
|
|
6ad3d40216 | ||
|
|
d6d498bc8f | ||
|
|
6df7f6d6ec | ||
|
|
5365182c86 | ||
|
|
e5121b33e6 | ||
|
|
5fb4eacf5b | ||
|
|
c4c520c1a3 | ||
|
|
9eab9ad40d | ||
|
|
a2fef11016 | ||
|
|
088eb3da14 | ||
|
|
a0706b8780 | ||
|
|
0e1dfcf408 | ||
|
|
3ca9f5f389 | ||
|
|
5acd225f0f | ||
|
|
1708bb5cdf | ||
|
|
8127484081 | ||
|
|
103c75e78c | ||
|
|
d886dcc7c3 | ||
|
|
ea54d73911 | ||
|
|
1ef4cc9fc2 | ||
|
|
4785db4471 | ||
|
|
c8cbc55b59 | ||
|
|
8d501e1c24 | ||
|
|
132a5d424d | ||
|
|
4c5f20fde4 | ||
|
|
35a20fb197 | ||
|
|
63848e8fc2 | ||
|
|
16a7409ce1 | ||
|
|
c23955d0b5 | ||
|
|
18ef355d2a | ||
|
|
def1eb0b5f | ||
|
|
bdbc189a4f | ||
|
|
6efe9efab8 | ||
|
|
7b72dde222 | ||
|
|
5948a0b235 | ||
|
|
a6fc7ba07a | ||
|
|
a326adc863 | ||
|
|
ab31c633d0 | ||
|
|
48254a93f0 | ||
|
|
365c8c18d7 | ||
|
|
bbc0695a8f | ||
|
|
1d0f8f01f2 | ||
|
|
2253439940 | ||
|
|
c1838a60df | ||
|
|
8a6713d5c0 | ||
|
|
201efecafa | ||
|
|
79f061be93 | ||
|
|
4786c1c59f | ||
|
|
116cad88a0 | ||
|
|
7d1200c434 | ||
|
|
99a9707a34 | ||
|
|
787f2390fb | ||
|
|
a510ac4052 | ||
|
|
9f414baa99 | ||
|
|
086711e467 | ||
|
|
bf2d6dd0fa | ||
|
|
4c43226b99 | ||
|
|
dfe8aac10b | ||
|
|
983786c29f | ||
|
|
edb81425cf | ||
|
|
69478aea58 | ||
|
|
e9d9c0c42d | ||
|
|
b41adc2eee | ||
|
|
115d6e29df | ||
|
|
83781f3a70 | ||
|
|
5f1ec550ec | ||
|
|
95f8dccc14 | ||
|
|
0134ec7b04 | ||
|
|
590938fa33 | ||
|
|
46f5f21368 | ||
|
|
b6bf3800c7 | ||
|
|
6043d37b05 | ||
|
|
34919b0396 | ||
|
|
846613c244 | ||
|
|
c4c137dc08 | ||
|
|
130bb19a11 | ||
|
|
d0f14d7a3c | ||
|
|
795c2cf540 | ||
|
|
bcd02ce718 | ||
|
|
469efc923b | ||
|
|
dcd74f6922 | ||
|
|
4b7eaf6dae | ||
|
|
085f909f35 | ||
|
|
4f394b683d | ||
|
|
7096ebedbc | ||
|
|
0b0243a5e0 | ||
|
|
5c9f9b1685 | ||
|
|
bf74bb196d | ||
|
|
c6e14caa31 | ||
|
|
506602b257 | ||
|
|
a79ec0d408 | ||
|
|
140724be2e | ||
|
|
04af1f3c97 | ||
|
|
cb4f1daac1 | ||
|
|
96bab5697d | ||
|
|
abac3e7758 | ||
|
|
8557cb5305 | ||
|
|
735840aafd | ||
|
|
1c777888d5 | ||
|
|
625810cd8a | ||
|
|
cd63abd72e | ||
|
|
d911b776bf | ||
|
|
9e7d1b3ed8 | ||
|
|
53735f2026 | ||
|
|
a43b31400f | ||
|
|
e15f2ac8ab | ||
|
|
06e760081c | ||
|
|
fc8637c81a | ||
|
|
01eaf48471 | ||
|
|
238a075c6a | ||
|
|
1d130b4a89 | ||
|
|
95d935d917 | ||
|
|
c4db8d37c2 | ||
|
|
17e0154995 | ||
|
|
d60c9800c2 | ||
|
|
04d2542b81 | ||
|
|
9a25cb3ee7 | ||
|
|
1e22b8e567 | ||
|
|
d05dfb18a7 | ||
|
|
a5c6ddb8ac | ||
|
|
90bff709a4 | ||
|
|
052132ec37 | ||
|
|
d42361bac1 | ||
|
|
2df19bcbb4 | ||
|
|
0ef4251462 | ||
|
|
a4af81563a | ||
|
|
36cd63836e | ||
|
|
48f9959fcd | ||
|
|
9effa3d2ab | ||
|
|
0782222c6b | ||
|
|
f7784b6543 | ||
|
|
fabc9e5d1c | ||
|
|
06805d70fe | ||
|
|
123e317e52 | ||
|
|
68a9855506 | ||
|
|
688a3251a9 | ||
|
|
30c5cc1dc4 | ||
|
|
4ab1d5ca7f | ||
|
|
5a808a0f91 | ||
|
|
a6cc31944a | ||
|
|
41a9e5f710 | ||
|
|
31790e0bb7 | ||
|
|
4e0c8e218d | ||
|
|
4fe4c0c72a | ||
|
|
f171357e36 | ||
|
|
ef91cc992e | ||
|
|
04b92f2ad2 | ||
|
|
4f049112de | ||
|
|
86242b322c | ||
|
|
14af95001e | ||
|
|
2dd56f5bda | ||
|
|
c250c632f3 | ||
|
|
e3fb4f8799 | ||
|
|
b4f704d7f1 | ||
|
|
9ee2c6be57 | ||
|
|
7de8f71f58 | ||
|
|
b6a75093b7 | ||
|
|
d54dda40d3 | ||
|
|
a705c714ab | ||
|
|
0e48837eec | ||
|
|
5e5ba54c3e | ||
|
|
4403b139d5 | ||
|
|
103974cae4 | ||
|
|
aea37467d8 | ||
|
|
9d2ed7bc5f | ||
|
|
bfb11d249e | ||
|
|
f7dbda4ed3 | ||
|
|
7c9c6ea3df | ||
|
|
7d49c4de87 | ||
|
|
e49a36c9fd | ||
|
|
236e773438 | ||
|
|
e3144c3093 | ||
|
|
cbd8409611 | ||
|
|
a85b38850c | ||
|
|
e01f1ae8ed | ||
|
|
d60a4c5f87 | ||
|
|
9c1fe2c922 | ||
|
|
3131c0d0ac | ||
|
|
364e9cf3de | ||
|
|
5a84863873 | ||
|
|
006b2b18b0 | ||
|
|
fb262e38a7 | ||
|
|
f8151284ee | ||
|
|
5d8c91b687 | ||
|
|
0f23462607 | ||
|
|
ca50ea190f | ||
|
|
31192abd2c | ||
|
|
42c7f41d24 | ||
|
|
fade03e337 | ||
|
|
d511d90a2f | ||
|
|
54d6cafda9 | ||
|
|
a730cd1c51 | ||
|
|
cccd75fc42 | ||
|
|
dbc16a6c9b | ||
|
|
6ffad6043f | ||
|
|
4e398950a0 | ||
|
|
f5e51897e3 | ||
|
|
698ea36cc2 | ||
|
|
0cf9cdd3b1 |
@@ -863,7 +863,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/34064225?v=4",
|
||||
"profile": "https://github.com/CronKz",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -883,6 +884,853 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cepacs",
|
||||
"name": "cepacs",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/36515590?v=4",
|
||||
"profile": "https://github.com/cepacs",
|
||||
"contributions": [
|
||||
"bug",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lea-mink",
|
||||
"name": "lea-mink",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/37537300?v=4",
|
||||
"profile": "https://github.com/lea-mink",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hannahtinkler",
|
||||
"name": "Hannah Tinkler",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/7140719?v=4",
|
||||
"profile": "https://github.com/hannahtinkler",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "doekman",
|
||||
"name": "Doeke Zanstra",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1086388?v=4",
|
||||
"profile": "https://github.com/doekman",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SjamonDaal",
|
||||
"name": "Djamon Staal",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4325936?v=4",
|
||||
"profile": "https://www.sdhd.nl/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EarlRamirez",
|
||||
"name": "Earl Ramirez",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/12306859?v=4",
|
||||
"profile": "https://github.com/EarlRamirez",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RichardRay",
|
||||
"name": "Richard Ray Thomas",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/8671456?v=4",
|
||||
"profile": "https://github.com/RichardRay",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "thelamer",
|
||||
"name": "Ryan Kuba",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1852688?v=4",
|
||||
"profile": "https://www.taisun.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ParadoxGuitarist",
|
||||
"name": "Brian Monroe",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6751928?v=4",
|
||||
"profile": "https://github.com/ParadoxGuitarist",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "plexorama",
|
||||
"name": "plexorama",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/605167?v=4",
|
||||
"profile": "https://github.com/plexorama",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tilldeeke",
|
||||
"name": "Till Deeke",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1795149?v=4",
|
||||
"profile": "https://tilldeeke.de",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "5quirrel",
|
||||
"name": "5quirrel",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/12634129?v=4",
|
||||
"profile": "https://github.com/5quirrel",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jasonlshelton",
|
||||
"name": "Jason",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/13071957?v=4",
|
||||
"profile": "https://github.com/jasonlshelton",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chemfy",
|
||||
"name": "Antti",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/7128321?v=4",
|
||||
"profile": "https://github.com/chemfy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DeusMaximus",
|
||||
"name": "DeusMaximus",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/10080364?v=4",
|
||||
"profile": "https://github.com/DeusMaximus",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "A-ROYAL",
|
||||
"name": "a-royal",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/16384611?v=4",
|
||||
"profile": "https://github.com/A-ROYAL",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "albertoaldrigo",
|
||||
"name": "Alberto Aldrigo",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5358208?v=4",
|
||||
"profile": "https://github.com/albertoaldrigo",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RealEnder",
|
||||
"name": "Alex Stanev",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1412342?v=4",
|
||||
"profile": "http://alex.stanev.org/blog",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sirrus",
|
||||
"name": "Andreas Rehm",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/177295?v=4",
|
||||
"profile": "http://devel.itsolution2.de",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xelan",
|
||||
"name": "Andreas Erhard",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5080535?v=4",
|
||||
"profile": "https://github.com/xelan",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "angeldeejay",
|
||||
"name": "Andrés Vanegas Jiménez",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/142350?v=4",
|
||||
"profile": "https://github.com/angeldeejay",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aschiavon91",
|
||||
"name": "Antonio Schiavon",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3910403?v=4",
|
||||
"profile": "https://github.com/aschiavon91",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benunter",
|
||||
"name": "benunter",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10464547?v=4",
|
||||
"profile": "https://github.com/benunter",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rudashi",
|
||||
"name": "Borys Żmuda",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5038647?v=4",
|
||||
"profile": "http://catweb24.pl",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chibacityblues",
|
||||
"name": "chibacityblues",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5539359?v=4",
|
||||
"profile": "https://github.com/chibacityblues",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cwlin0416",
|
||||
"name": "Chien Wei Lin",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1954830?v=4",
|
||||
"profile": "https://github.com/cwlin0416",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Againstreality",
|
||||
"name": "Christian Schuster",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/11700533?v=4",
|
||||
"profile": "https://github.com/Againstreality",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kopi-item",
|
||||
"name": "Christian Stefanus",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4308704?v=4",
|
||||
"profile": "http://chriss.webhostid.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wxcafe",
|
||||
"name": "wxcafé",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3009327?v=4",
|
||||
"profile": "http://wxcafe.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dpyroc",
|
||||
"name": "dpyroc",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/35761525?v=4",
|
||||
"profile": "https://github.com/dpyroc",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "da-friedl",
|
||||
"name": "Daniel Friedlmaier",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2153639?v=4",
|
||||
"profile": "http://www.friedlmaier.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danielheene",
|
||||
"name": "Daniel Heene",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2947640?v=4",
|
||||
"profile": "https://github.com/danielheene",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danielcb",
|
||||
"name": "danielcb",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/319022?v=4",
|
||||
"profile": "https://github.com/danielcb",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dominiksenti",
|
||||
"name": "Dominik Senti",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/15846537?v=4",
|
||||
"profile": "https://github.com/dominiksenti",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EpixFr",
|
||||
"name": "Eric Gautheron",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/25570954?v=4",
|
||||
"profile": "http://www.konectik.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Erlpil",
|
||||
"name": "Erlend Pilø",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5732623?v=4",
|
||||
"profile": "https://erlpil.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "frapposelli",
|
||||
"name": "Fabio Rapposelli",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/541832?v=4",
|
||||
"profile": "http://fabio.technology",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fgbs",
|
||||
"name": "Felipe Barros",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3605240?v=4",
|
||||
"profile": "https://github.com/fgbs",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "possebon",
|
||||
"name": "Fernando Possebon",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/257745?v=4",
|
||||
"profile": "https://github.com/possebon",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gdraque",
|
||||
"name": "gdraque",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/2540832?v=4",
|
||||
"profile": "https://github.com/gdraque",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "georgwallisch",
|
||||
"name": "Georg Wallisch",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/23440381?v=4",
|
||||
"profile": "https://github.com/georgwallisch",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jgroblesr85",
|
||||
"name": "Gerardo Robles",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/9852832?v=4",
|
||||
"profile": "https://github.com/jgroblesr85",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mrgluek",
|
||||
"name": "Gluek",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/11082640?v=4",
|
||||
"profile": "https://t.me/Gluek",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AdnanAbuShahad",
|
||||
"name": "AdnanAbuShahad",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/6847946?v=4",
|
||||
"profile": "https://github.com/AdnanAbuShahad",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hafidzi",
|
||||
"name": "Hafidzi My",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/3580608?v=4",
|
||||
"profile": "https://hafidzi.my",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fofwisdom",
|
||||
"name": "Harim Park",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/205521?v=4",
|
||||
"profile": "https://github.com/fofwisdom",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Kentsson",
|
||||
"name": "Henrik Kentsson",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3333841?v=4",
|
||||
"profile": "http://www.kentsson.se",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "husnulyaqien",
|
||||
"name": "Husnul Yaqien",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/36551034?v=4",
|
||||
"profile": "https://github.com/husnulyaqien",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "abaalkh",
|
||||
"name": "Ibrahim",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2372747?v=4",
|
||||
"profile": "http://abaalkhail.org",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "igolman",
|
||||
"name": "igolman",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1389334?v=4",
|
||||
"profile": "https://github.com/igolman",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "itangiang",
|
||||
"name": "itangiang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/3257070?v=4",
|
||||
"profile": "https://github.com/itangiang",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jarby1211",
|
||||
"name": "jarby1211",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/14814254?v=4",
|
||||
"profile": "https://github.com/jarby1211",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JohnWillker",
|
||||
"name": "Jhonn Willker",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/6719357?v=4",
|
||||
"profile": "http://jwillker.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joxelito94",
|
||||
"name": "Jose",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/10983635?v=4",
|
||||
"profile": "https://github.com/joxelito94",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "laopangzi",
|
||||
"name": "laopangzi",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5206122?v=4",
|
||||
"profile": "https://github.com/laopangzi",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lstrojny",
|
||||
"name": "Lars Strojny",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/79707?v=4",
|
||||
"profile": "http://usrportage.de",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MarcosBL",
|
||||
"name": "MarcosBL",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/389801?v=4",
|
||||
"profile": "http://twitter.com/marcosbl",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mariejoyacajes",
|
||||
"name": "marie joy cajes",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/35664606?v=4",
|
||||
"profile": "https://github.com/mariejoyacajes",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "msjohansen",
|
||||
"name": "Mark S. Johansen",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3052816?v=4",
|
||||
"profile": "http://www.markjohansen.dk",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "stubben",
|
||||
"name": "Martin Stub",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/982885?v=4",
|
||||
"profile": "http://martinstub.dk",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "meyerf99",
|
||||
"name": "Meyer Flavio",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/28959963?v=4",
|
||||
"profile": "https://github.com/meyerf99",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MicaelRodrigues",
|
||||
"name": "Micael Rodrigues",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/796443?v=4",
|
||||
"profile": "https://github.com/MicaelRodrigues",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mikaelssen",
|
||||
"name": "Mikael Rasmussen",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10481331?v=4",
|
||||
"profile": "http://rubixy.com/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "IxFail",
|
||||
"name": "IxFail",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1544552?v=4",
|
||||
"profile": "https://github.com/IxFail",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MohammedFota",
|
||||
"name": "Mohammed Fota",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/18483118?v=4",
|
||||
"profile": "http://www.mohammedfota.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "omego",
|
||||
"name": "Moayad Alserihi",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/227080?v=4",
|
||||
"profile": "https://github.com/omego",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "saymd",
|
||||
"name": "saymd",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1680266?v=4",
|
||||
"profile": "https://github.com/saymd",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pooot",
|
||||
"name": "Patrik Larsson",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1826808?v=4",
|
||||
"profile": "https://nordsken.se",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "drcryo",
|
||||
"name": "drcryo",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/20584746?v=4",
|
||||
"profile": "https://github.com/drcryo",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pawel1615",
|
||||
"name": "pawel1615",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/19408004?v=4",
|
||||
"profile": "https://github.com/pawel1615",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bodrovics",
|
||||
"name": "bodrovics",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/23340468?v=4",
|
||||
"profile": "https://github.com/bodrovics",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "priatna",
|
||||
"name": "priatna",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3257654?v=4",
|
||||
"profile": "https://github.com/priatna",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ProfFan",
|
||||
"name": "Fan Jiang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5358374?v=4",
|
||||
"profile": "https://amayume.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ragnarcx",
|
||||
"name": "ragnarcx",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22555451?v=4",
|
||||
"profile": "https://github.com/ragnarcx",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "reinvanhaaren",
|
||||
"name": "Rein van Haaren",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/18654582?v=4",
|
||||
"profile": "http://www.reinvanhaaren.nl/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dheche",
|
||||
"name": "Teguh Dwicaksana",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/386672?v=4",
|
||||
"profile": "http://dheche.songolimo.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FRaccie",
|
||||
"name": "fraccie",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/2572552?v=4",
|
||||
"profile": "https://github.com/FRaccie",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vinzruzell",
|
||||
"name": "vinzruzell",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/35182720?v=4",
|
||||
"profile": "https://github.com/vinzruzell",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vipsystem",
|
||||
"name": "Kevin Austin",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7883603?v=4",
|
||||
"profile": "http://kevinaustin.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wira-sandy",
|
||||
"name": "Wira Sandy",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3861828?v=4",
|
||||
"profile": "http://azuraweb.xyz",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "GrayHoax",
|
||||
"name": "Илья",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/8663789?v=4",
|
||||
"profile": "https://github.com/GrayHoax",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "godusevpn",
|
||||
"name": "GodUseVPN",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/30119111?v=4",
|
||||
"profile": "https://github.com/godusevpn",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EngrZhou",
|
||||
"name": "周周",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/745576?v=4",
|
||||
"profile": "https://github.com/EngrZhou",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "takuy",
|
||||
"name": "Sam",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1631095?v=4",
|
||||
"profile": "https://github.com/takuy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Azerothian",
|
||||
"name": "Azerothian",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/264022?v=4",
|
||||
"profile": "https://www.illisian.com.au",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timothyfarmer",
|
||||
"name": "Tim Farmer",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7632599?v=4",
|
||||
"profile": "https://github.com/timothyfarmer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mskrip",
|
||||
"name": "Marián Skrip",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/17459600?v=4",
|
||||
"profile": "https://github.com/mskrip",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Godmartinz",
|
||||
"name": "Godfrey Martinez",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/47435081?v=4",
|
||||
"profile": "https://github.com/Godmartinz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bigtreeEdo",
|
||||
"name": "bigtreeEdo",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2075128?v=4",
|
||||
"profile": "https://github.com/bigtreeEdo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ColinMcNeil",
|
||||
"name": "Colin McNeil",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5000430?v=4",
|
||||
"profile": "https://colinmcneil.me/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JoKneeMo",
|
||||
"name": "JoKneeMo",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/421625?v=4",
|
||||
"profile": "https://github.com/JoKneeMo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joshi-redbridge",
|
||||
"name": "Joshi",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/54849013?v=4",
|
||||
"profile": "http://www.redbridge.se",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "anthonypburns",
|
||||
"name": "Anthony Burns",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/15731458?v=4",
|
||||
"profile": "https://github.com/anthonypburns",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alek13",
|
||||
"name": "Alexander Chibrikin",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1972329?v=4",
|
||||
"profile": "http://phpprofi.ru/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
20
.env.example
20
.env.example
@@ -7,6 +7,7 @@ APP_KEY=ChangeMe
|
||||
APP_URL=null
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
MAX_RESULTS=500
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DATABASE SETTINGS
|
||||
@@ -25,6 +26,7 @@ DB_COLLATION=utf8mb4_unicode_ci
|
||||
# OPTIONAL: SSL DATABASE SETTINGS
|
||||
# --------------------------------------------
|
||||
DB_SSL=false
|
||||
DB_SSL_IS_PAAS=false
|
||||
DB_SSL_KEY_PATH=null
|
||||
DB_SSL_CERT_PATH=null
|
||||
DB_SSL_CA_PATH=null
|
||||
@@ -43,6 +45,7 @@ MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME='Snipe-IT'
|
||||
MAIL_REPLYTO_ADDR=you@example.com
|
||||
MAIL_REPLYTO_NAME='Snipe-IT'
|
||||
MAIL_BACKUP_NOTIFICATION_ADDRESS=you@example.com
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: IMAGE LIBRARY
|
||||
@@ -63,8 +66,11 @@ SECURE_COOKIES=false
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SECURITY HEADER SETTINGS
|
||||
# --------------------------------------------
|
||||
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
|
||||
ALLOW_IFRAMING=false
|
||||
REFERRER_POLICY=same-origin
|
||||
ENABLE_CSP=false
|
||||
CORS_ALLOWED_ORIGINS=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: CACHE SETTINGS
|
||||
@@ -72,13 +78,20 @@ ENABLE_CSP=false
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
CACHE_PREFIX=snipeit
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: REDIS SETTINGS
|
||||
# --------------------------------------------
|
||||
REDIS_HOST=null
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT-null
|
||||
REDIS_PORT=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MEMCACHED SETTINGS
|
||||
# --------------------------------------------
|
||||
MEMCACHED_HOST=null
|
||||
MEMCACHED_PORT=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: AWS S3 SETTINGS
|
||||
@@ -101,7 +114,8 @@ APP_LOG=single
|
||||
APP_LOG_MAX_FILES=10
|
||||
APP_LOCKED=false
|
||||
FILESYSTEM_DISK=local
|
||||
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1
|
||||
ALLOW_IFRAMING=false
|
||||
APP_CIPHER=AES-256-CBC
|
||||
GOOGLE_MAPS_API=
|
||||
BACKUP_ENV=true
|
||||
LDAP_MEM_LIM=500M
|
||||
LDAP_TIME_LIM=600
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://snipe-it.localapp
|
||||
DB_CONNECTION=sqlite_testing
|
||||
DB_DEFAULT=sqlite_testing
|
||||
DB_CONNECTION=mysql
|
||||
DB_DEFAULT=mysql
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=snipeittests
|
||||
DB_USERNAME=snipeit
|
||||
|
||||
19
.env.unit-tests
Normal file
19
.env.unit-tests
Normal file
@@ -0,0 +1,19 @@
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://snipe-it.localapp
|
||||
DB_CONNECTION=sqlite_testing
|
||||
DB_DEFAULT=sqlite_testing
|
||||
DB_HOST=localhost
|
||||
APP_KEY=base64:tu9NRh/a6+dCXBDGvg0Gv/0TcABnFsbT4AKxrr8mwQo=
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: LOGIN THROTTLING
|
||||
# (LOGIN_LOCKOUT_DURATIONin minutes)
|
||||
# --------------------------------------------
|
||||
LOGIN_MAX_ATTEMPTS=1000000
|
||||
LOGIN_LOCKOUT_DURATION=100000000
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
MAIL_FROM_NAME=Snipe-IT
|
||||
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# You can add one username per supported platform and one custom link
|
||||
# patreon: # Replace with your Patreon username
|
||||
# open_collective: # Replace with your Open Collective username
|
||||
# ko_fi: # Replace with your Ko-fi username
|
||||
custom: https://snipeitapp.com/donate
|
||||
61
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
61
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
#### Please confirm you have done the following before posting your bug report:
|
||||
|
||||
- [ ] I have enabled debug mode
|
||||
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- Snipe-IT Version
|
||||
- OS: [e.g. Ubuntu, CentOS]
|
||||
- Web Server: [e.g. Apache, IIS]
|
||||
- PHP Version
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Error Messages**
|
||||
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
|
||||
- If a stacktrace is provided in the error, include that too.
|
||||
- Any errors that appear in your browser's error console.
|
||||
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
|
||||
- Include any additional information you can find in `storage/logs` and your webserver's logs.
|
||||
|
||||
**Additional context**
|
||||
- Is this a fresh install or an upgrade?
|
||||
- What OS and web server you're running Snipe-IT on
|
||||
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
|
||||
- Include what you've done so far in the installation, and if you got any error messages along the way.
|
||||
- Indicate whether or not you've manually edited any data directly in the database
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.
|
||||
23
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- Snipe-IT Version
|
||||
- OS: [e.g. Ubuntu, CentOS]
|
||||
- Web Server: [e.g. Apache, IIS]
|
||||
- PHP Version
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
32
.github/stale.yml
vendored
32
.github/stale.yml
vendored
@@ -6,14 +6,38 @@ daysUntilClose: 7
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- ready for dev
|
||||
- bounty
|
||||
- :woman_technologist: ready for dev
|
||||
- :moneybag: bounty
|
||||
- :hand: bug
|
||||
- "🔐 security"
|
||||
- "👩💻 ready for dev"
|
||||
- "💰 bounty"
|
||||
- "✋ bug"
|
||||
|
||||
exemptMilestones: true
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
only: issues
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
unmarkComment: >
|
||||
Okay, it looks like this issue or feature request might still be important. We'll re-open
|
||||
it for now. Thank you for letting us know!
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Is this still relevant? We haven't heard from anyone in a bit. If so,
|
||||
please comment with any updates or additional detail.
|
||||
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
recent activity. It will be closed if no further activity occurs. Don't
|
||||
take it personally, we just need to keep a handle on things. Thank you
|
||||
for your contributions!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity. If you believe this is still an issue, please confirm that
|
||||
this issue is still happening in the most recent version of Snipe-IT and reply
|
||||
to this thread to re-open it.
|
||||
|
||||
1
.github/travis-memory.ini
vendored
Normal file
1
.github/travis-memory.ini
vendored
Normal file
@@ -0,0 +1 @@
|
||||
memory_limit= 2048M
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -50,3 +50,4 @@ tests/_support/_generated/*
|
||||
/storage/oauth-public.key
|
||||
|
||||
*.cache
|
||||
/public/storage
|
||||
|
||||
22
.travis.yml
22
.travis.yml
@@ -1,4 +1,7 @@
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token:
|
||||
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
|
||||
hosts:
|
||||
- localhost
|
||||
sudo: false
|
||||
@@ -11,13 +14,17 @@ services:
|
||||
|
||||
# list any PHP version you want to test against
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1.2
|
||||
- 7.2
|
||||
- 7.1.4
|
||||
- 7.3
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.3
|
||||
|
||||
# execute any number of scripts before the test run, custom env's are available as variables
|
||||
before_script:
|
||||
- phpenv config-add .github/travis-memory.ini
|
||||
- phantomjs --webdriver=4444 &
|
||||
- sleep 4
|
||||
- mysql -e 'CREATE DATABASE snipeit_unit;'
|
||||
@@ -47,9 +54,12 @@ before_script:
|
||||
script:
|
||||
- ./vendor/bin/codecept run unit
|
||||
# - ./vendor/bin/codecept run acceptance --env=testing-ci
|
||||
- ./vendor/bin/codecept run functional --env=functional-travis
|
||||
#script: ./vendor/bin/codecept run
|
||||
- ./vendor/bin/codecept run api --env=testing-ci
|
||||
- ./vendor/bin/codecept run functional --env=functional-travis -g func1
|
||||
- ./vendor/bin/codecept run functional --env=functional-travis -g func2
|
||||
- ./vendor/bin/codecept run api --env=functional-travis
|
||||
|
||||
after_script:
|
||||
- vendor/bin/test-reporter
|
||||
|
||||
after_success:
|
||||
- codecov
|
||||
|
||||
49
Dockerfile
49
Dockerfile
@@ -1,24 +1,27 @@
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Brady Wetherington <uberbrady@gmail.com>
|
||||
LABEL maintainer="uberbrady, hinchk"
|
||||
|
||||
RUN apt-get update && apt-get install -y software-properties-common
|
||||
RUN LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apache2 \
|
||||
apache2-bin \
|
||||
libapache2-mod-php7.0 \
|
||||
php7.0-curl \
|
||||
php7.0-ldap \
|
||||
php7.0-mysql \
|
||||
php7.0-mcrypt \
|
||||
php7.0-gd \
|
||||
php7.0-xml \
|
||||
php7.0-mbstring \
|
||||
php7.0-zip \
|
||||
php7.0-bcmath \
|
||||
libapache2-mod-php7.1 \
|
||||
php7.1-curl \
|
||||
php7.1-ldap \
|
||||
php7.1-mysql \
|
||||
php7.1-mcrypt \
|
||||
php7.1-gd \
|
||||
php7.1-xml \
|
||||
php7.1-mbstring \
|
||||
php7.1-zip \
|
||||
php7.1-bcmath \
|
||||
patch \
|
||||
curl \
|
||||
vim \
|
||||
git \
|
||||
mysql-client \
|
||||
supervisor \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
@@ -26,8 +29,8 @@ RUN phpenmod mcrypt
|
||||
RUN phpenmod gd
|
||||
RUN phpenmod bcmath
|
||||
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.0/apache2/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.0/cli/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.1/apache2/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.1/cli/php.ini
|
||||
|
||||
RUN useradd -m --uid 1000 --gid 50 docker
|
||||
|
||||
@@ -64,7 +67,12 @@ RUN chown -R docker /var/www/html
|
||||
RUN \
|
||||
rm -r "/var/www/html/storage/private_uploads" && ln -fs "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" \
|
||||
&& rm -rf "/var/www/html/public/uploads" && ln -fs "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" \
|
||||
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups"
|
||||
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
|
||||
&& mkdir "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
|
||||
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
|
||||
&& chown docker "/var/lib/snipeit/keys/" \
|
||||
&& chmod +x /var/www/html/artisan \
|
||||
&& echo "Finished setting up application in /var/www/html"
|
||||
|
||||
############## DEPENDENCIES via COMPOSER ###################
|
||||
|
||||
@@ -91,16 +99,11 @@ VOLUME ["/var/lib/snipeit"]
|
||||
|
||||
##### START SERVER
|
||||
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
COPY docker/startup.sh docker/supervisord.conf /
|
||||
COPY docker/supervisor-exit-event-listener /usr/bin/supervisor-exit-event-listener
|
||||
RUN chmod +x /startup.sh /usr/bin/supervisor-exit-event-listener
|
||||
|
||||
# Add Tini
|
||||
ENV TINI_VERSION v0.14.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
|
||||
CMD ["/entrypoint.sh"]
|
||||
CMD ["/startup.sh"]
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
56
README.md
56
README.md
@@ -1,17 +1,16 @@
|
||||
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeyhead) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors)
|
||||
[](https://www.codetriage.com/snipe/snipe-it)
|
||||
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
|
||||
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
This is a FOSS project for asset management in IT Operations. Knowing who has which laptop, when it was purchased in order to depreciate it correctly, handling software licenses, etc.
|
||||
|
||||
It is built on [Laravel 5.4](http://laravel.com).
|
||||
It is built on [Laravel 5.5](http://laravel.com).
|
||||
|
||||
Snipe-IT is actively developed and we're [releasing 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/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
|
||||
|
||||
__This is web-based software__. This means there 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, 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.
|
||||
__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, 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.
|
||||
|
||||
-----
|
||||
|
||||
@@ -51,6 +50,30 @@ Please see the [translations documentation](https://snipe-it.readme.io/docs/tran
|
||||
|
||||
-----
|
||||
|
||||
### Libraries, Modules & Related Projects
|
||||
|
||||
Since the release of the JSON REST API, several third-party developers have been developing modules and libraries to work with Snipe-IT.
|
||||
|
||||
- [Python Module](https://github.com/jbloomer/SnipeIT-PythonAPI) by [@jbloomer](https://github.com/jbloomer)
|
||||
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
|
||||
- [InQRy](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
|
||||
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
|
||||
- [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
|
||||
- [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
|
||||
- [Snipe-IT plugin for Jira Service Desk (beta)](https://marketplace.atlassian.com/apps/1220379/snipe-it-for-jira-service-desk-beta?hosting=cloud&tab=overview) - for the upcoming Snipe-IT v5 only
|
||||
- [Python 3 CSV importer](https://github.com/gastamper/snipeit-csvimporter) - allows importing assets into Snipe-IT based on Item Name rather than Asset Tag.
|
||||
- [Snipe-IT Kubernetes Helm Chart](https://github.com/t3n/helm-charts/tree/master/snipeit) - For more information, [click here](https://hub.helm.sh/charts/t3n/snipeit).
|
||||
|
||||
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
|
||||
|
||||
-----
|
||||
|
||||
### Security
|
||||
|
||||
To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.
|
||||
|
||||
-----
|
||||
|
||||
### Contributors
|
||||
|
||||
Thanks goes to all of these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)) who have helped Snipe-IT get this far:
|
||||
@@ -70,7 +93,20 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") | [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars2.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
@@ -83,9 +119,3 @@ Please see the documentation on [contributing and developing for Snipe-IT](https
|
||||
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
-----
|
||||
|
||||
### Security
|
||||
|
||||
To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.
|
||||
|
||||
84
Vagrantfile
vendored
Normal file
84
Vagrantfile
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
|
||||
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define "bionic" do |bionic|
|
||||
bionic.vm.box = "ubuntu/bionic64"
|
||||
bionic.vm.hostname = 'bionic'
|
||||
bionic.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
bionic.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
bionic.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "xenial" do |xenial|
|
||||
xenial.vm.box = "ubuntu/xenial64"
|
||||
xenial.vm.hostname = 'xenial'
|
||||
xenial.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
xenial.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
xenial.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "trusty" do |trusty|
|
||||
trusty.vm.box = "ubuntu/trusty32"
|
||||
trusty.vm.hostname = 'trusty'
|
||||
trusty.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
trusty.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
trusty.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "centos7" do |centos7|
|
||||
centos7.vm.box = "centos/7"
|
||||
centos7.vm.hostname = 'centos7'
|
||||
centos7.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
centos7.vm.provision :shell, :inline => "sudo yum -y update"
|
||||
centos7.vm.provision :shell, :inline => "yum install -y wget"
|
||||
centos7.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
centos7.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "centos6" do |centos6|
|
||||
centos6.vm.box = "centos/6"
|
||||
centos6.vm.hostname = 'centos6'
|
||||
centos6.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
centos6.vm.provision :shell, :inline => "sudo yum -y update"
|
||||
centos6.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
centos6.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "jessie" do |jessie|
|
||||
jessie.vm.box = "debian/jessie64"
|
||||
jessie.vm.hostname = 'debian8'
|
||||
jessie.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
jessie.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
jessie.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "stretch" do |stretch|
|
||||
stretch.vm.box = "debian/stretch64"
|
||||
stretch.vm.hostname = 'debian9'
|
||||
stretch.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
stretch.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
stretch.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "fedora27" do |fedora27|
|
||||
fedora27.vm.box = "fedora/27-cloud-base"
|
||||
fedora27.vm.hostname = 'fedora27'
|
||||
fedora27.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
fedora27.vm.provision :shell, :inline => "dnf -y install wget"
|
||||
fedora27.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
fedora27.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "fedora26" do |fedora26|
|
||||
fedora26.vm.box = "fedora/26-cloud-base"
|
||||
fedora26.vm.hostname = 'fedora26'
|
||||
fedora26.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
fedora26.vm.provision :shell, :inline => "dnf -y install wget"
|
||||
fedora26.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
fedora26.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
end
|
||||
0
_config.yml
Normal file
0
_config.yml
Normal file
160
app/Console/Commands/ImportLocations.php
Normal file
160
app/Console/Commands/ImportLocations.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use League\Csv\Reader;
|
||||
use App\Models\Location;
|
||||
|
||||
class ImportLocations extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:import-locations {filename}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import locations and their parents';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
|
||||
if (!ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$filename = $this->argument('filename');
|
||||
$csv = Reader::createFromPath(storage_path('private_uploads/imports/').$filename, 'r');
|
||||
$this->info('Attempting to process: '.storage_path('private_uploads/imports/').$filename);
|
||||
$csv->setHeaderOffset(0); //because we don't want to insert the header
|
||||
$results = $csv->getRecords();
|
||||
|
||||
// Import parent location names first if they don't exist
|
||||
foreach ($results as $parent_index => $parent_row) {
|
||||
|
||||
if (array_key_exists('Parent Name', $parent_row)) {
|
||||
$parent_name = trim($parent_row['Parent Name']);
|
||||
if (array_key_exists('Name', $parent_row)) {
|
||||
$this->info('- Parent: ' . $parent_name . ' in row as: ' . trim($parent_row['Parent Name']));
|
||||
}
|
||||
|
||||
// Save parent location name
|
||||
// This creates a sort of name-stub that we'll update later on in this script
|
||||
$parent_location = Location::firstOrCreate(array('name' => $parent_name));
|
||||
if (array_key_exists('Name', $parent_row)) {
|
||||
$this->info('Parent for ' . $parent_row['Name'] . ' is ' . $parent_name . '. Attempting to save ' . $parent_name . '.');
|
||||
}
|
||||
|
||||
// Check if the record was updated or created.
|
||||
// This is mostly for clearer debugging.
|
||||
if ($parent_location->exists) {
|
||||
$this->info('- Parent location '.$parent_name.' already exists.');
|
||||
} else {
|
||||
$this->info('- Parent location '.$parent_name.' was created.');
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->info('- No Parent Name provided, so no parent location will be created.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->info('----- Parents Created.... backfilling additional details... --------');
|
||||
// Loop through ALL records and add/update them if there are additional fields
|
||||
// besides name
|
||||
foreach ($results as $index => $row) {
|
||||
|
||||
if (array_key_exists('Parent Name', $row)) {
|
||||
$parent_name = trim($row['Parent Name']);
|
||||
}
|
||||
|
||||
// Set the location attributes to save
|
||||
if (array_key_exists('Name', $row)) {
|
||||
$location = Location::firstOrNew(array('name' => trim($row['Name'])));
|
||||
$location->name = trim($row['Name']);
|
||||
$this->info('Checking location: '.$location->name);
|
||||
} else {
|
||||
$this->error('Location name is required and is missing from at least one row in this dataset. Check your CSV for extra trailing rows and try again.');
|
||||
return false;
|
||||
}
|
||||
if (array_key_exists('Currency', $row)) {
|
||||
$location->currency = trim($row['Currency']);
|
||||
}
|
||||
if (array_key_exists('Address 1', $row)) {
|
||||
$location->address = trim($row['Address 1']);
|
||||
}
|
||||
if (array_key_exists('Address 2', $row)) {
|
||||
$location->address2 = trim($row['Address 2']);
|
||||
}
|
||||
if (array_key_exists('City', $row)) {
|
||||
$location->city = trim($row['City']);
|
||||
}
|
||||
if (array_key_exists('State', $row)) {
|
||||
$location->state = trim($row['State']);
|
||||
}
|
||||
if (array_key_exists('Zip', $row)) {
|
||||
$location->zip = trim($row['Zip']);
|
||||
}
|
||||
if (array_key_exists('Country', $row)) {
|
||||
$location->country = trim($row['Country']);
|
||||
}
|
||||
if (array_key_exists('Country', $row)) {
|
||||
$location->ldap_ou = trim($row['OU']);
|
||||
}
|
||||
|
||||
|
||||
// If a parent name is provided, we created it earlier in the script,
|
||||
// so let's grab that ID
|
||||
if ($parent_name) {
|
||||
$this->info('-- Searching for Parent Name: '.$parent_name);
|
||||
$parent = Location::where('name', '=', $parent_name)->first();
|
||||
$location->parent_id = $parent->id;
|
||||
$this->info('Parent: '.$parent_name.' - ID: '.$parent->id);
|
||||
}
|
||||
|
||||
// Make sure the more advanced (non-name) fields pass validation
|
||||
if (($location->isValid()) && ($location->save())) {
|
||||
|
||||
// Check if the record was updated or created.
|
||||
// This is mostly for clearer debugging.
|
||||
if ($location->exists) {
|
||||
$this->info('Location ' . $location->name . ' already exists. Updating...');
|
||||
} else {
|
||||
$this->info('- Location '.$location->name.' was created. ');
|
||||
}
|
||||
|
||||
// If there's a validation error, display that
|
||||
} else {
|
||||
$this->error('- Non-parent Location '.$location->name.' could not be created: '.$location->getErrors() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,8 @@ class LdapSync extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', '500M');
|
||||
|
||||
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
|
||||
$ldap_result_username = Setting::getSettings()->ldap_username_field;
|
||||
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
|
||||
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
|
||||
@@ -61,16 +60,16 @@ class LdapSync extends Command
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::error($e);
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
$summary = array();
|
||||
|
||||
try {
|
||||
try {
|
||||
if ($this->option('base_dn') != '') {
|
||||
$search_base = $this->option('base_dn');
|
||||
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
} else {
|
||||
$search_base = null;
|
||||
}
|
||||
@@ -80,7 +79,7 @@ class LdapSync extends Command
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::error($e);
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -106,7 +105,7 @@ class LdapSync extends Command
|
||||
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
|
||||
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
|
||||
$ldap_ou_lengths = array();
|
||||
|
||||
|
||||
foreach ($ldap_ou_locations as $location) {
|
||||
$ldap_ou_lengths[] = strlen($location["ldap_ou"]);
|
||||
}
|
||||
@@ -128,15 +127,21 @@ class LdapSync extends Command
|
||||
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
|
||||
$usernames = array();
|
||||
for ($i = 0; $i < $location_users["count"]; $i++) {
|
||||
$location_users[$i]["ldap_location_override"] = true;
|
||||
$location_users[$i]["location_id"] = $ldap_loc["id"];
|
||||
$usernames[] = $location_users[$i][$ldap_result_username][0];
|
||||
|
||||
if (array_key_exists($ldap_result_username, $location_users[$i])) {
|
||||
$location_users[$i]["ldap_location_override"] = true;
|
||||
$location_users[$i]["location_id"] = $ldap_loc["id"];
|
||||
$usernames[] = $location_users[$i][$ldap_result_username][0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Delete located users from the general group.
|
||||
foreach ($results as $key => $generic_entry) {
|
||||
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
|
||||
unset($results[$key]);
|
||||
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
|
||||
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
|
||||
unset($results[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,38 +167,49 @@ class LdapSync extends Command
|
||||
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
|
||||
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
|
||||
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
$enabled_accounts = [
|
||||
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224'
|
||||
];
|
||||
$item['activated'] = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
|
||||
$user = User::where('username', $item["username"])->first();
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item["createorupdate"] = 'updated';
|
||||
} else {
|
||||
$item['activated'] = 0;
|
||||
}
|
||||
|
||||
// User exists
|
||||
$item["createorupdate"] = 'updated';
|
||||
if (!$user = User::where('username', $item["username"])->first()) {
|
||||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $pass;
|
||||
$user->activated = 0;
|
||||
$item["createorupdate"] = 'created';
|
||||
}
|
||||
|
||||
// Create the user if they don't exist.
|
||||
$user->first_name = e($item["firstname"]);
|
||||
$user->last_name = e($item["lastname"]);
|
||||
$user->username = e($item["username"]);
|
||||
$user->email = e($item["email"]);
|
||||
$user->first_name = $item["firstname"];
|
||||
$user->last_name = $item["lastname"];
|
||||
$user->username = $item["username"];
|
||||
$user->email = $item["email"];
|
||||
$user->employee_num = e($item["employee_number"]);
|
||||
$user->activated = $item['activated'];
|
||||
|
||||
// Sync activated state for Active Directory.
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
$enabled_accounts = [
|
||||
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224', '4260352'
|
||||
];
|
||||
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
// If we're not using AD, and there isn't an activated flag set, activate all users
|
||||
elseif (empty($ldap_result_active_flag)) {
|
||||
$user->activated = 1;
|
||||
}
|
||||
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
} elseif ((isset($location)) && (!empty($location))) {
|
||||
$user->location_id = e($location->id);
|
||||
|
||||
if ((is_array($location)) && (array_key_exists('id', $location))) {
|
||||
$user->location_id = $location['id'];
|
||||
} elseif (is_object($location)) {
|
||||
$user->location_id = $location->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$user->notes = 'Imported from LDAP';
|
||||
$user->ldap_import = 1;
|
||||
|
||||
$errors = '';
|
||||
|
||||
@@ -65,7 +65,7 @@ class ObjectImportCommand extends Command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
public function handle()
|
||||
{
|
||||
$filename = $this->argument('filename');
|
||||
$class = title_case($this->option('item-type'));
|
||||
@@ -74,6 +74,7 @@ class ObjectImportCommand extends Command
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId($this->option('user_id'))
|
||||
->setUpdating($this->option('update'))
|
||||
->setShouldNotify($this->option('send-welcome'))
|
||||
->setUsernameFormat($this->option('username_format'));
|
||||
|
||||
$logFile = $this->option('logfile');
|
||||
@@ -172,6 +173,7 @@ class ObjectImportCommand extends Command
|
||||
array('web-importer', null, InputOption::VALUE_NONE, 'Internal: packages output for use with the web importer'),
|
||||
array('user_id', null, InputOption::VALUE_REQUIRED, 'ID of user creating items', 1),
|
||||
array('update', null, InputOption::VALUE_NONE, 'If a matching item is found, update item information'),
|
||||
array('send-welcome', null, InputOption::VALUE_NONE, 'Whether to send a welcome email to any new users that are created.'),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class PaveIt extends Command
|
||||
DB::statement('delete from accessories_users');
|
||||
DB::statement('delete from asset_logs');
|
||||
DB::statement('delete from asset_maintenances');
|
||||
DB::statement('delete from login_attempts');
|
||||
DB::statement('delete from asset_uploads');
|
||||
DB::statement('delete from action_logs');
|
||||
DB::statement('delete from checkout_requests');
|
||||
|
||||
@@ -33,7 +33,7 @@ class Purge extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Purge all soft-deleted deleted records in the database. This will rewrite history for items that have been edited, or checked in or out. It will also reqrite history for users associated with deleted items.';
|
||||
protected $description = 'Purge all soft-deleted deleted records in the database. This will rewrite history for items that have been edited, or checked in or out. It will also rewrite history for users associated with deleted items.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
|
||||
126
app/Console/Commands/ReEncodeCustomFieldNames.php
Normal file
126
app/Console/Commands/ReEncodeCustomFieldNames.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ReEncodeCustomFieldNames extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:regenerate-fieldnames';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This utility will regenerate the column names for custom fields. It should typically only be needed when a PHP upgrade changed the behavior of the unicode conversion between versions.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* All three of these things must match for the custom fields system to work as expected:
|
||||
*
|
||||
* - what the system thinks the output of $field->convertUnicodeDbSlug() is
|
||||
* - the actual db_column name in the customfields table
|
||||
* - the physical column name that was created on the assets table
|
||||
*
|
||||
* For some people who upgraded their version of PHP, the unicode converter now behaves
|
||||
* differently in than it did when their custom fields were first created, specifically as it
|
||||
* relates to handling slashes, ampersands, etc. This can result in the field names no longer
|
||||
* matching up, as an older version of the PHP extension simply dropped slashes, etc, while the
|
||||
* newer version of the PHP extension will convert them to underscores.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if ($this->confirm('This will regenerate all of the custom field database fieldnames in your database. THIS WILL CHANGE YOUR SCHEMA AND SHOULD NOT BE DONE WITHOUT MAKING A BACKUP FIRST. Do you wish to continue?'))
|
||||
{
|
||||
|
||||
/** Get all of the custom fields */
|
||||
$fields = CustomField::get();
|
||||
|
||||
$asset_columns = \DB::getSchemaBuilder()->getColumnListing('assets');
|
||||
$custom_field_columns = array();
|
||||
|
||||
/** Loop through the columns on the assets table */
|
||||
foreach ($asset_columns as $asset_column) {
|
||||
|
||||
/** Add ones that start with _snipeit_ to an array for handling */
|
||||
if (strpos($asset_column, '_snipeit_') === 0) {
|
||||
|
||||
/**
|
||||
* Get the ID of the custom field based on the fieldname.
|
||||
* For example, in _snipeit_mac_address_1, we grab the 1 because we know
|
||||
* that's the ID of the custom field that created the column.
|
||||
* Then use that ID as the array key for use comparing the actual assets field name
|
||||
* and the db_column value from the custom fields table.
|
||||
*/
|
||||
$last_part = substr(strrchr($asset_column, "_snipeit_"), 1);
|
||||
$custom_field_columns[$last_part] = $asset_column;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
|
||||
$this->info($field->name .' ('.$field->id.') column should be '. $field->convertUnicodeDbSlug().'');
|
||||
|
||||
/** The assets table has the column it should have, all is well */
|
||||
if (\Schema::hasColumn('assets', $field->convertUnicodeDbSlug()))
|
||||
{
|
||||
$this->info('-- ✓ This field exists - all good');
|
||||
|
||||
/**
|
||||
* There is a mismatch between the fieldname on the assets table and
|
||||
* what $field->convertUnicodeDbSlug() is *now* expecting.
|
||||
*/
|
||||
} else {
|
||||
$this->warn('-- X Field mismatch: updating... ');
|
||||
|
||||
/** Make sure the custom_field_columns array has the ID */
|
||||
if (array_key_exists($field->id, $custom_field_columns)) {
|
||||
|
||||
/**
|
||||
* Update the asset schema to the corrected fieldname that will be recognized by the
|
||||
* system elsewhere that we use $field->convertUnicodeDbSlug()
|
||||
*/
|
||||
\Schema::table('assets', function($table) use ($custom_field_columns, $field) {
|
||||
$table->renameColumn($custom_field_columns[$field->id], $field->convertUnicodeDbSlug());
|
||||
});
|
||||
|
||||
$this->warn('-- ✓ Field updated from '.$custom_field_columns[$field->id].' to '.$field->convertUnicodeDbSlug());
|
||||
|
||||
} else {
|
||||
$this->warn('-- X WARNING: There is no field on the assets table ending in '.$field->id.'. This may require more in-depth investigation and may mean the schema was altered manually.');
|
||||
}
|
||||
}
|
||||
|
||||
/** Update the db_column property in the custom fields table, just in case it doesn't match the other
|
||||
* things.
|
||||
*/
|
||||
$field->db_column = $field->convertUnicodeDbSlug();
|
||||
$field->save();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ class ResetDemoSettings extends Command
|
||||
$settings->ldap_enabled = 0;
|
||||
$settings->full_multiple_companies_support = 1;
|
||||
$settings->alt_barcode = 'C128';
|
||||
$settings->skin = '';
|
||||
$settings->email_domain = 'snipeitapp.com';
|
||||
$settings->email_format = 'filastname';
|
||||
$settings->username_format = 'filastname';
|
||||
@@ -62,6 +63,8 @@ class ResetDemoSettings extends Command
|
||||
$settings->time_display_format = 'g:iA';
|
||||
$settings->thumbnail_max_h = '30';
|
||||
$settings->locale = 'en';
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = 'on';
|
||||
$settings->save();
|
||||
|
||||
if ($user = User::where('username', '=', 'admin')->first()) {
|
||||
|
||||
120
app/Console/Commands/RestoreDeletedUsers.php
Normal file
120
app/Console/Commands/RestoreDeletedUsers.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\License;
|
||||
use DB;
|
||||
use Artisan;
|
||||
|
||||
class RestoreDeletedUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:restore-users {--start_date=} {--end_date=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Restore users, and any associated assets and license checkouts.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$start_date = $this->option('start_date');
|
||||
$end_date = $this->option('end_date');
|
||||
$asset_totals = 0;
|
||||
$license_totals = 0;
|
||||
$user_count = 0;
|
||||
|
||||
|
||||
if (($start_date=='') || ($end_date=='')) {
|
||||
$this->info('ERROR: All fields are required.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$users = User::whereBetween('deleted_at', [$start_date, $end_date])->withTrashed()->get();
|
||||
$this->info('There are '.$users->count().' users deleted between '.$start_date.' and '.$end_date);
|
||||
$this->warn('Making a backup!');
|
||||
Artisan::call('backup:run');
|
||||
|
||||
foreach ($users as $user) {
|
||||
$user_count++;
|
||||
$user_logs = Actionlog::where('target_id', $user->id)->where('target_type',User::class)
|
||||
->where('action_type','checkout')->with('item')->get();
|
||||
|
||||
$this->info($user_count.'. '.$user->username.' ('.$user->id.') was deleted at '.$user->deleted_at. ' and has '.$user_logs->count().' checkouts associated.');
|
||||
|
||||
foreach ($user_logs as $user_log) {
|
||||
$this->info(' * '.$user_log->item_type.': '.$user_log->item->name.' - item_id: '.$user_log->item_id);
|
||||
|
||||
if ($user_log->item_type==Asset::class) {
|
||||
$asset_totals++;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $user_log->item_id)
|
||||
->update(['assigned_to' => $user->id, 'assigned_type'=> User::class]);
|
||||
|
||||
$this->info(' ** Asset '.$user_log->item->id.' ('.$user_log->item->asset_tag.') restored to user '.$user->id.'');
|
||||
|
||||
} elseif ($user_log->item_type==License::class) {
|
||||
$license_totals++;
|
||||
|
||||
$avail_seat = DB::table('license_seats')->where('license_id','=',$user_log->item->id)
|
||||
->whereNull('assigned_to')->whereNull('asset_id')->whereBetween('updated_at', [$start_date, $end_date])->first();
|
||||
if ($avail_seat) {
|
||||
$this->info(' ** Allocating seat '.$avail_seat->id.' for this License');
|
||||
|
||||
DB::table('license_seats')
|
||||
->where('id', $avail_seat->id)
|
||||
->update(['assigned_to' => $user->id]);
|
||||
|
||||
} else {
|
||||
$this->warn('ERROR: No available seats for '.$user_log->item->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->warn('Restoring user '.$user->username.'!');
|
||||
$user->restore();
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->info($asset_totals.' assets affected');
|
||||
$this->info($license_totals.' licenses affected');
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
135
app/Console/Commands/RotateAppKey.php
Normal file
135
app/Console/Commands/RotateAppKey.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Artisan;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use \Illuminate\Encryption\Encrypter;
|
||||
|
||||
class RotateAppKey extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:rotate-key';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* 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 MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ")) {
|
||||
|
||||
|
||||
|
||||
// Get the existing app_key and ciphers
|
||||
// We put them in a variable since we clear the cache partway through here.
|
||||
$old_app_key = config('app.key');
|
||||
$cipher = config('app.cipher');
|
||||
|
||||
// Generate a new one
|
||||
Artisan::call('key:generate', ['--show' => true]);
|
||||
$new_app_key = Artisan::output();
|
||||
|
||||
// Clear the config cache
|
||||
Artisan::call('config:clear');
|
||||
|
||||
$this->warn('Your app cipher is: '.$cipher);
|
||||
$this->warn('Your old APP_KEY is: '.$old_app_key);
|
||||
$this->warn('Your new APP_KEY is: '.$new_app_key);
|
||||
|
||||
// Write the new app key to the .env file
|
||||
$this->writeNewEnvironmentFileWith($new_app_key);
|
||||
|
||||
// Manually create an old encrypter instance using the old app key
|
||||
// and also create a new encrypter instance so we can re-crypt the field
|
||||
// using the newly generated app key
|
||||
$oldEncrypter = new Encrypter(base64_decode(substr($old_app_key, 7)), $cipher);
|
||||
$newEncrypter = new Encrypter(base64_decode(substr($new_app_key, 7)), $cipher);
|
||||
|
||||
$fields = CustomField::where('field_encrypted', '1')->get();
|
||||
|
||||
|
||||
foreach ($fields as $field) {
|
||||
|
||||
$assets = Asset::whereNotNull($field->db_column)->get();
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
|
||||
$this->line('DECRYPTED: '. $field->db_column);
|
||||
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
|
||||
$this->line('ENCRYPTED: '.$field->db_column);
|
||||
$asset->save();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle the LDAP password if one is provided
|
||||
$setting = Setting::first();
|
||||
if ($setting->ldap_pword!='') {
|
||||
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
|
||||
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
|
||||
$setting->save();
|
||||
$this->warn('LDAP password has been re-encrypted.');
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$this->info('This operation has been canceled. No changes have been made.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new environment file with the given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
protected function writeNewEnvironmentFileWith($key)
|
||||
{
|
||||
|
||||
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
|
||||
$this->keyReplacementPattern(),
|
||||
'APP_KEY='.$key,
|
||||
file_get_contents($this->laravel->environmentFilePath())
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a regex pattern that will match env APP_KEY with any random key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function keyReplacementPattern()
|
||||
{
|
||||
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
|
||||
return "/^APP_KEY{$escaped}/m";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,10 @@ namespace App\Console\Commands;
|
||||
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use App\Notifications\ExpectedCheckinAdminNotification;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
@@ -40,22 +42,29 @@ class SendExpectedCheckinAlerts extends Command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$whenNotify = Carbon::now()->addDays(7);
|
||||
$assets = Asset::with('assignedTo')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
|
||||
$assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
|
||||
|
||||
$this->info($whenNotify.' is deadline');
|
||||
$this->info($assets->count().' assets');
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->assignedTo && $asset->checkoutOutToUser()) {
|
||||
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
|
||||
//$this->info($asset);
|
||||
if ($asset->assigned && $asset->checkedOutToUser()) {
|
||||
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
|
||||
}
|
||||
}
|
||||
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use DB;
|
||||
use App\Notifications\ExpiringLicenseNotification;
|
||||
use App\Notifications\ExpiringAssetsNotification;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -41,91 +43,44 @@ class SendExpirationAlerts extends Command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
public function handle()
|
||||
{
|
||||
|
||||
// Expiring Assets
|
||||
$expiring_assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
$this->info(count($expiring_assets).' expiring assets');
|
||||
|
||||
$asset_data['count'] = count($expiring_assets);
|
||||
$asset_data['email_content'] ='';
|
||||
$now = date("Y-m-d");
|
||||
$settings = Setting::getSettings();
|
||||
$threshold = $settings->alert_interval;
|
||||
|
||||
|
||||
foreach ($expiring_assets as $asset) {
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
|
||||
|
||||
$expires = $asset->present()->warrantee_expires();
|
||||
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
|
||||
|
||||
if ($difference > 30) {
|
||||
$asset_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
|
||||
} else {
|
||||
$asset_data['email_content'] .= '<tr style="background-color:#d9534f;">';
|
||||
}
|
||||
$asset_data['email_content'] .= '<td><a href="'.config('app.url').'/hardware/'.e($asset->id).'/view">';
|
||||
$asset_data['email_content'] .= $asset->present()->name().'</a></td><td>'.e($asset->asset_tag).'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.e($asset->present()->warrantee_expires()).'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.$difference.' '.trans('mail.days').'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.($asset->supplier ? e($asset->supplier->name) : '').'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.($asset->assignedTo ? e($asset->assignedTo->present()->name()) : '').'</td>';
|
||||
$asset_data['email_content'] .= '</tr>';
|
||||
}
|
||||
|
||||
// Expiring licenses
|
||||
$expiring_licenses = License::getExpiringLicenses(Setting::getSettings()->alert_interval);
|
||||
$this->info(count($expiring_licenses).' expiring licenses');
|
||||
|
||||
|
||||
$license_data['count'] = count($expiring_licenses);
|
||||
$license_data['email_content'] = '';
|
||||
|
||||
foreach ($expiring_licenses as $license) {
|
||||
$expires = $license->expiration_date;
|
||||
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
|
||||
|
||||
if ($difference > 30) {
|
||||
$license_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
|
||||
} else {
|
||||
$license_data['email_content'] .= '<tr style="background-color:#d9534f;">';
|
||||
}
|
||||
$license_data['email_content'] .= '<td><a href="'.route('licenses.show', $license->id).'">';
|
||||
$license_data['email_content'] .= $license->name.'</a></td>';
|
||||
$license_data['email_content'] .= '<td>'.$license->expiration_date.'</td>';
|
||||
$license_data['email_content'] .= '<td>'.$difference.' days</td>';
|
||||
$license_data['email_content'] .= '</tr>';
|
||||
}
|
||||
|
||||
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
|
||||
|
||||
|
||||
if (count($expiring_assets) > 0) {
|
||||
$this->info('Report sent to '.Setting::getSettings()->alert_email);
|
||||
\Mail::send('emails.expiring-assets-report', $asset_data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Expiring_Assets_Report'));
|
||||
});
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
// Expiring Assets
|
||||
$assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
if ($assets->count() > 0) {
|
||||
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
|
||||
}
|
||||
|
||||
if (count($expiring_licenses) > 0) {
|
||||
$this->info('Report sent to '.Setting::getSettings()->alert_email);
|
||||
\Mail::send('emails.expiring-licenses-report', $license_data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Expiring_Licenses_Report'));
|
||||
});
|
||||
// Expiring licenses
|
||||
$licenses = License::getExpiringLicenses($threshold);
|
||||
|
||||
|
||||
if ($licenses->count() > 0) {
|
||||
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
if (Setting::getSettings()->alert_email=='') {
|
||||
echo "Could not send email. No alert email configured in settings. \n";
|
||||
} elseif (Setting::getSettings()->alerts_enabled!=1) {
|
||||
echo "Alerts are disabled in the settings. No mail will be sent. \n";
|
||||
if ($settings->alert_email=='') {
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif ($settings->alerts_enabled!=1) {
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Setting;
|
||||
use DB;
|
||||
use Mail;
|
||||
use App\Helpers\Helper;
|
||||
use App\Notifications\InventoryAlert;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -42,25 +43,28 @@ class SendInventoryAlerts extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$data['data'] = Helper::checkLowInventory();
|
||||
$data['count'] = count($data['data']);
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled==1)) {
|
||||
|
||||
if (count($data['data']) > 0) {
|
||||
\Mail::send('emails.low-inventory', $data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Low_Inventory_Report'));
|
||||
$items = Helper::checkLowInventory();
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
|
||||
if (($items) && (count($items) > 0)) {
|
||||
$this->info(trans_choice('mail.low_inventory_alert', count($items)));
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Setting::getSettings()->alert_email=='') {
|
||||
echo "Could not send email. No alert email configured in settings. \n";
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif (Setting::getSettings()->alerts_enabled!=1) {
|
||||
echo "Alerts are disabled in the settings. No mail will be sent. \n";
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
91
app/Console/Commands/SendUpcomingAuditReport.php
Normal file
91
app/Console/Commands/SendUpcomingAuditReport.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Notifications\ExpiringAssetsNotification;
|
||||
use App\Models\Recipients;
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Notifications\SendUpcomingAuditNotification;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SendUpcomingAuditReport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:upcoming-audits';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send email/slack notifications for upcoming asset audits.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->alert_email != '') && ($settings->audit_warning_days) && ($settings->alerts_enabled == 1)) {
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
|
||||
// Assets due for auditing
|
||||
|
||||
$assets = Asset::whereNotNull('next_audit_date')
|
||||
->DueOrOverdueForAudit($settings)
|
||||
->orderBy('last_audit_date', 'asc')->get();
|
||||
|
||||
if ($assets->count() > 0) {
|
||||
|
||||
$this->info(trans_choice('mail.upcoming-audits', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $settings->audit_warning_days]));
|
||||
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
|
||||
$this->info('Audit report sent to '.$settings->alert_email);
|
||||
} else {
|
||||
$this->info('No assets to be audited. No report sent.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
} elseif ($settings->alert_email=='') {
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif (!$settings->audit_warning_days) {
|
||||
$this->error('No audit warning days set in Admin Notifications. No mail will be sent.');
|
||||
} elseif ($settings->alerts_enabled!=1) {
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
} else {
|
||||
$this->error('Something went wrong. :( ');
|
||||
$this->error('Admin Notifications Email Setting: '.$settings->alert_email);
|
||||
$this->error('Admin Audit Warning Setting: '.$settings->audit_warning_days);
|
||||
$this->error('Admin Alerts Emnabled: '.$settings->alerts_enabled);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
76
app/Console/Commands/SyncAssetCounters.php
Normal file
76
app/Console/Commands/SyncAssetCounters.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Asset;
|
||||
|
||||
class SyncAssetCounters extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:counter-sync';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Syncs checkedout, checked in, and requested counters for assets';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$start = microtime(true);
|
||||
$assets = Asset::withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')
|
||||
->withTrashed()->get();
|
||||
|
||||
if ($assets) {
|
||||
if ($assets->count() > 0) {
|
||||
$bar = $this->output->createProgressBar($assets->count());
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$asset->checkin_counter = (int) $asset->checkins_count;
|
||||
$asset->checkout_counter = (int) $asset->checkouts_count;
|
||||
$asset->requests_counter = (int) $asset->user_requests_count;
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->save();
|
||||
$output['info'][] = 'Asset: ' . $asset->id . ' has ' . $asset->checkin_counter . ' checkins, ' . $asset->checkout_counter . ' checkouts, and ' . $asset->requests_counter . ' requests';
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
|
||||
foreach ($output['info'] as $key => $output_text) {
|
||||
$this->info($output_text);
|
||||
}
|
||||
|
||||
$time_elapsed_secs = microtime(true) - $start;
|
||||
$this->info('Sync executed in ' . $time_elapsed_secs . ' seconds');
|
||||
|
||||
} else {
|
||||
$this->info('No assets to sync');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class SyncAssetLocations extends Command
|
||||
$output['info'][] = 'There are '.$assigned_user_assets->count().' assets checked out to users.';
|
||||
foreach ($assigned_user_assets as $assigned_user_asset) {
|
||||
if (($assigned_user_asset->assignedTo) && ($assigned_user_asset->assignedTo->userLoc)) {
|
||||
$new_location=$assigned_user_asset->assignedTo->userloc->id;
|
||||
$new_location = $assigned_user_asset->assignedTo->userLoc->id;
|
||||
$output['info'][] ='Setting User Asset ' . $assigned_user_asset->id . ' ('.$assigned_user_asset->asset_tag.') to ' . $assigned_user_asset->assignedTo->userLoc->name . ' which is id: ' . $new_location;
|
||||
} else {
|
||||
$output['warn'][] ='Asset ' . $assigned_user_asset->id . ' ('.$assigned_user_asset->asset_tag.') still has no location! ';
|
||||
|
||||
@@ -36,7 +36,7 @@ class SystemBackup extends Command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
$this->call('backup:run');
|
||||
|
||||
@@ -2,34 +2,14 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\ImportLocations;
|
||||
use App\Console\Commands\ReEncodeCustomFieldNames;
|
||||
use App\Console\Commands\RestoreDeletedUsers;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\PaveIt::class,
|
||||
Commands\CreateAdmin::class,
|
||||
Commands\SendExpirationAlerts::class,
|
||||
Commands\SendInventoryAlerts::class,
|
||||
Commands\SendExpectedCheckinAlerts::class,
|
||||
Commands\ObjectImportCommand::class,
|
||||
Commands\Version::class,
|
||||
Commands\SystemBackup::class,
|
||||
Commands\DisableLDAP::class,
|
||||
Commands\Purge::class,
|
||||
Commands\LdapSync::class,
|
||||
Commands\FixDoubleEscape::class,
|
||||
Commands\RecryptFromMcrypt::class,
|
||||
Commands\ResetDemoSettings::class,
|
||||
Commands\SyncAssetLocations::class,
|
||||
Commands\RegenerateAssetTags::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
@@ -45,10 +25,12 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command('snipeit:expected-checkin')->daily();
|
||||
$schedule->command('snipeit:backup')->weekly();
|
||||
$schedule->command('backup:clean')->daily();
|
||||
$schedule->command('snipeit:upcoming-audits')->daily();
|
||||
}
|
||||
|
||||
protected function commands()
|
||||
{
|
||||
require base_path('routes/console.php');
|
||||
$this->load(__DIR__.'/Commands');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,17 @@ namespace App\Exceptions;
|
||||
use Exception;
|
||||
class CheckoutNotAllowed extends Exception
|
||||
{
|
||||
|
||||
private $errorMessage;
|
||||
|
||||
function __construct($errorMessage = null)
|
||||
{
|
||||
$this->errorMessage = $errorMessage;
|
||||
|
||||
parent::__construct($errorMessage);
|
||||
}
|
||||
public function __toString()
|
||||
{
|
||||
return "A checkout is not allowed under these circumstances";
|
||||
return is_null($this->errorMessage) ? "A checkout is not allowed under these circumstances" : $this->errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class Handler extends ExceptionHandler
|
||||
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
|
||||
\Illuminate\Session\TokenMismatchException::class,
|
||||
\Illuminate\Validation\ValidationException::class,
|
||||
\Intervention\Image\Exception\NotSupportedException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -66,10 +67,6 @@ class Handler extends ExceptionHandler
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200);
|
||||
}
|
||||
|
||||
if ($e instanceof \Illuminate\Validation\ValidationException) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $e->response['messages'], 400));
|
||||
}
|
||||
|
||||
if ($this->isHttpException($e)) {
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
@@ -84,11 +81,6 @@ class Handler extends ExceptionHandler
|
||||
|
||||
}
|
||||
}
|
||||
// Try to parse 500 Errors in a bit nicer way when debug is enabled.
|
||||
if (config('app.debug')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, "An Error has occured! " . $e->getMessage()), 500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -117,4 +109,16 @@ class Handler extends ExceptionHandler
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a validation exception into a JSON response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Validation\ValidationException $exception
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function invalidJson($request, ValidationException $exception)
|
||||
{
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors(), 400));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,95 +137,6 @@ class Helper
|
||||
return floatval($floatString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of models in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function modelList()
|
||||
{
|
||||
$models = AssetModel::with('manufacturer')->get();
|
||||
$model_array[''] = trans('general.select_model');
|
||||
foreach ($models as $model) {
|
||||
$model_array[$model->id] = $model->present()->modelName();
|
||||
}
|
||||
return $model_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of companies in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function companyList()
|
||||
{
|
||||
$company_list = array('' => trans('general.select_company')) + DB::table('companies')
|
||||
->orderBy('name', 'asc')
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
return $company_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of categories in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function categoryList($category_type = null)
|
||||
{
|
||||
$categories = Category::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('name', 'asc');
|
||||
if (!empty($category_type)) {
|
||||
$categories = $categories->where('category_type', '=', $category_type);
|
||||
}
|
||||
$category_list = array('' => trans('general.select_category')) + $categories->pluck('name', 'id')->toArray();
|
||||
return $category_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of categories in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function departmentList()
|
||||
{
|
||||
$departments = Department::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('name', 'asc');
|
||||
|
||||
return array('' => trans('general.select_department')) + $departments->pluck('name', 'id')->toArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of suppliers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function suppliersList()
|
||||
{
|
||||
$supplier_list = array('' => trans('general.select_supplier')) + Supplier::orderBy('name', 'asc')
|
||||
->orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $supplier_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of status labels in an array to make a dropdown menu
|
||||
*
|
||||
@@ -235,42 +146,11 @@ class Helper
|
||||
*/
|
||||
public static function statusLabelList()
|
||||
{
|
||||
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('deployable', 'desc')
|
||||
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('default_label', 'desc')->orderBy('name','asc')->orderBy('deployable','desc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $statuslabel_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of locations in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function locationsList()
|
||||
{
|
||||
$location_list = array('' => trans('general.select_location')) + Location::orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $location_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of manufacturers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function manufacturerList()
|
||||
{
|
||||
$manufacturer_list = array('' => trans('general.select_manufacturer')) +
|
||||
Manufacturer::orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $manufacturer_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of status label types in an array to make a dropdown menu
|
||||
*
|
||||
@@ -289,24 +169,6 @@ class Helper
|
||||
return $statuslabel_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of managers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function managerList()
|
||||
{
|
||||
$manager_list = array('' => trans('general.select_user')) +
|
||||
User::where('deleted_at', '=', null)
|
||||
->orderBy('last_name', 'asc')
|
||||
->orderBy('first_name', 'asc')->get()
|
||||
->pluck('complete_name', 'id')->toArray();
|
||||
|
||||
return $manager_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of depreciations in an array to make a dropdown menu
|
||||
*
|
||||
@@ -330,58 +192,17 @@ class Helper
|
||||
*/
|
||||
public static function categoryTypeList()
|
||||
{
|
||||
$category_types = array('' => '','accessory' => 'Accessory', 'asset' => 'Asset', 'consumable' => 'Consumable','component' => 'Component');
|
||||
$category_types = array(
|
||||
'' => '',
|
||||
'accessory' => 'Accessory',
|
||||
'asset' => 'Asset',
|
||||
'consumable' => 'Consumable',
|
||||
'component' => 'Component',
|
||||
'license' => 'License'
|
||||
);
|
||||
return $category_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function usersList()
|
||||
{
|
||||
$users_list = array( '' => trans('general.select_user')) +
|
||||
Company::scopeCompanyables(User::where('deleted_at', '=', null))
|
||||
->where('show_in_list', '=', 1)
|
||||
->orderBy('last_name', 'asc')
|
||||
->orderBy('first_name', 'asc')->get()
|
||||
->pluck('complete_name', 'id')->toArray();
|
||||
|
||||
return $users_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of assets in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function assetsList()
|
||||
{
|
||||
$assets_list = array('' => trans('general.select_asset')) + Asset::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $assets_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detailed list of assets in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return array
|
||||
*/
|
||||
public static function detailedAssetList()
|
||||
{
|
||||
$assets = array('' => trans('general.select_asset')) + Company::scopeCompanyables(Asset::with('assignedTo', 'model'), 'assets.company_id')->get()->pluck('detailed_name', 'id')->toArray();
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of custom fields in an array to make a dropdown menu
|
||||
*
|
||||
@@ -406,7 +227,7 @@ class Helper
|
||||
{
|
||||
$keys = array_keys(CustomField::$PredefinedFormats);
|
||||
$stuff = array_combine($keys, $keys);
|
||||
return $stuff+["" => trans('admin/custom_fields/general.custom_format')];
|
||||
return $stuff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,16 +281,16 @@ class Helper
|
||||
*/
|
||||
public static function checkLowInventory()
|
||||
{
|
||||
$consumables = Consumable::withCount('consumableAssignments')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::withCount('users')->whereNotNull('min_amt')->get();
|
||||
$components = Component::withCount('assets')->whereNotNull('min_amt')->get();
|
||||
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
|
||||
$components = Component::withCount('assets as assets_count')->whereNotNull('min_amt')->get();
|
||||
|
||||
$avail_consumables = 0;
|
||||
$items_array = array();
|
||||
$all_count = 0;
|
||||
|
||||
foreach ($consumables as $consumable) {
|
||||
$avail = $consumable->qty - $consumable->consumable_assignment_count; //$consumable->numRemaining();
|
||||
$avail = $consumable->numRemaining();
|
||||
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
||||
if ($consumable->qty > 0) {
|
||||
$percent = number_format((($avail / $consumable->qty) * 100), 0);
|
||||
@@ -690,7 +511,7 @@ class Helper
|
||||
|
||||
$array['status'] = $status;
|
||||
$array['messages'] = $messages;
|
||||
if (($messages) && (count($messages) > 0)) {
|
||||
if (($messages) && (is_array($messages)) && (count($messages) > 0)) {
|
||||
$array['messages'] = $messages;
|
||||
}
|
||||
($payload) ? $array['payload'] = $payload : $array['payload'] = null;
|
||||
@@ -738,6 +559,7 @@ class Helper
|
||||
static $max_size = -1;
|
||||
|
||||
if ($max_size < 0) {
|
||||
|
||||
// Start with post_max_size.
|
||||
$post_max_size = Helper::parse_size(ini_get('post_max_size'));
|
||||
if ($post_max_size > 0) {
|
||||
@@ -751,6 +573,7 @@ class Helper
|
||||
$max_size = $upload_max;
|
||||
}
|
||||
}
|
||||
|
||||
return $max_size;
|
||||
}
|
||||
|
||||
@@ -787,6 +610,66 @@ class Helper
|
||||
}
|
||||
|
||||
|
||||
public static function filetype_icon($filename) {
|
||||
|
||||
$extension = substr(strrchr($filename,'.'),1);
|
||||
|
||||
if ($extension) {
|
||||
switch ($extension) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
case 'gif':
|
||||
case 'png':
|
||||
return "fa fa-file-image-o";
|
||||
break;
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
return "fa fa-file-word-o";
|
||||
break;
|
||||
case 'xls':
|
||||
case 'xlsx':
|
||||
return "fa fa-file-excel-o";
|
||||
break;
|
||||
case 'zip':
|
||||
case 'rar':
|
||||
return "fa fa-file-archive-o";
|
||||
break;
|
||||
case 'pdf':
|
||||
return "fa fa-file-pdf-o";
|
||||
break;
|
||||
case 'txt':
|
||||
return "fa fa-file-text-o";
|
||||
break;
|
||||
case 'lic':
|
||||
return "fa fa-floppy-o";
|
||||
break;
|
||||
default:
|
||||
return "fa fa-file-o";
|
||||
}
|
||||
}
|
||||
return "fa fa-file-o";
|
||||
}
|
||||
|
||||
public static function show_file_inline($filename) {
|
||||
|
||||
$extension = substr(strrchr($filename,'.'),1);
|
||||
|
||||
if ($extension) {
|
||||
switch ($extension) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
case 'gif':
|
||||
case 'png':
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use DB;
|
||||
use Gate;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Mail;
|
||||
use Redirect;
|
||||
use Illuminate\Http\Request;
|
||||
use Slack;
|
||||
@@ -86,26 +85,7 @@ class AccessoriesController extends Controller
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->user_id = Auth::user()->id;
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = "accessory-".str_random(18).'.'.$ext;
|
||||
$path = public_path('/uploads/accessories');
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
$accessory->image = $file_name;
|
||||
}
|
||||
}
|
||||
|
||||
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
|
||||
|
||||
|
||||
// Was the accessory created?
|
||||
@@ -166,30 +146,7 @@ class AccessoriesController extends Controller
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
|
||||
$image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = "accessory-".str_random(18).'.'.$ext;
|
||||
$path = public_path('/uploads/accessories');
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(null, 250, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) {
|
||||
unlink($path.'/'.$accessory->image);
|
||||
}
|
||||
|
||||
$accessory->image = $file_name;
|
||||
}
|
||||
}
|
||||
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
|
||||
|
||||
|
||||
// Was the accessory updated?
|
||||
@@ -241,7 +198,7 @@ class AccessoriesController extends Controller
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
}
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,10 +216,17 @@ class AccessoriesController extends Controller
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
if ($accessory->category) {
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
return view('accessories/checkout', compact('accessory'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'The category type for this accessory is not valid. Edit the accessory and select a valid accessory category.');
|
||||
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
return view('accessories/checkout', compact('accessory'));
|
||||
|
||||
}
|
||||
|
||||
@@ -313,16 +277,6 @@ class AccessoriesController extends Controller
|
||||
$data['expected_checkin'] = '';
|
||||
$data['note'] = $logaction->note;
|
||||
$data['require_acceptance'] = $accessory->requireAcceptance();
|
||||
// TODO: Port this to new mail notifications
|
||||
|
||||
if ((($accessory->requireAcceptance()=='1') || ($accessory->getEula())) && ($user->email!='')) {
|
||||
|
||||
Mail::send('emails.accept-accessory', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Confirm_accessory_delivery'));
|
||||
});
|
||||
}
|
||||
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
@@ -369,7 +323,7 @@ class AccessoriesController extends Controller
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
@@ -387,20 +341,12 @@ class AccessoriesController extends Controller
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = e($user->first_name);
|
||||
$data['last_name'] = e($user->last_name);
|
||||
$data['item_name'] = e($accessory->name);
|
||||
$data['checkin_date'] = e($logaction->created_at);
|
||||
$data['item_tag'] = '';
|
||||
$data['note'] = e($logaction->note);
|
||||
|
||||
if ((($accessory->checkin_email()=='1')) && ($user->email!='')) {
|
||||
|
||||
Mail::send('emails.checkin-asset', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Confirm_Accessory_Checkin'));
|
||||
});
|
||||
}
|
||||
|
||||
if ($backto=='user') {
|
||||
return redirect()->route("users.show", $return_to)->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ use App\Helpers\Helper;
|
||||
use App\Models\Accessory;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Models\Company;
|
||||
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Auth;
|
||||
use DB;
|
||||
|
||||
class AccessoriesController extends Controller
|
||||
{
|
||||
@@ -24,30 +27,36 @@ class AccessoriesController extends Controller
|
||||
$this->authorize('view', Accessory::class);
|
||||
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
|
||||
|
||||
$accessories = Accessory::whereNull('accessories.deleted_at')->with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$accessories->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('category_id')) {
|
||||
if ($request->filled('category_id')) {
|
||||
$accessories->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$accessories->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->has('supplier_id')) {
|
||||
if ($request->filled('supplier_id')) {
|
||||
$accessories->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -191,4 +200,94 @@ class AccessoriesController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save the Accessory checkout information.
|
||||
*
|
||||
* If Slack is enabled and/or asset acceptance is enabled, it will also
|
||||
* trigger a Slack message and send an email.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function checkout(Request $request, $accessoryId)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
|
||||
if ($accessory->numRemaining() > 0) {
|
||||
|
||||
if (!$user = User::find($request->input('assigned_to'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist')));
|
||||
}
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->assigned_to = $request->input('assigned_to');
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->get('assigned_to')
|
||||
]);
|
||||
|
||||
$accessory->logCheckout($request->input('note'), $user);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check in the item so that it can be checked out again to someone else
|
||||
*
|
||||
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param integer $accessoryUserId
|
||||
* @param string $backto
|
||||
* @return Redirect
|
||||
* @internal param int $accessoryId
|
||||
*/
|
||||
public function checkin(Request $request, $accessoryUserId = null)
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessoryUserId), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
if (!is_null($accessory_user->assigned_to)) {
|
||||
$user = User::find($accessory_user->assigned_to);
|
||||
}
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = $user->first_name;
|
||||
$data['last_name'] = $user->last_name;
|
||||
$data['item_name'] = $accessory->name;
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = '';
|
||||
$data['note'] = $logaction->note;
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkin.error')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,12 +40,17 @@ class AssetMaintenancesController extends Controller
|
||||
$maintenances = $maintenances->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if ($request->has('asset_id')) {
|
||||
if ($request->filled('asset_id')) {
|
||||
$maintenances->where('asset_id', '=', $request->input('asset_id'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($maintenances) && ($request->get('offset') > $maintenances->count())) ? $maintenances->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
@@ -56,6 +61,8 @@ class AssetMaintenancesController extends Controller
|
||||
'start_date',
|
||||
'completion_date',
|
||||
'notes',
|
||||
'asset_tag',
|
||||
'asset_name',
|
||||
'user_id'
|
||||
];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -65,6 +72,12 @@ class AssetMaintenancesController extends Controller
|
||||
case 'user_id':
|
||||
$maintenances = $maintenances->OrderAdmin($order);
|
||||
break;
|
||||
case 'asset_tag':
|
||||
$maintenances = $maintenances->OrderByTag($order);
|
||||
break;
|
||||
case 'asset_name':
|
||||
$maintenances = $maintenances->OrderByAssetName($order);
|
||||
break;
|
||||
default:
|
||||
$maintenances = $maintenances->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -32,22 +32,41 @@ class AssetModelsController extends Controller
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','assets_count'];
|
||||
|
||||
$assetmodels = AssetModel::select(['models.id','models.image','models.name','model_number','eol','models.notes','models.created_at','category_id','manufacturer_id','depreciation_id','fieldset_id', 'models.deleted_at'])
|
||||
$assetmodels = AssetModel::select([
|
||||
'models.id',
|
||||
'models.image',
|
||||
'models.name',
|
||||
'model_number',
|
||||
'eol',
|
||||
'models.notes',
|
||||
'models.created_at',
|
||||
'category_id',
|
||||
'manufacturer_id',
|
||||
'depreciation_id',
|
||||
'fieldset_id',
|
||||
'models.deleted_at',
|
||||
'models.updated_at',
|
||||
])
|
||||
->with('category','depreciation', 'manufacturer','fieldset')
|
||||
->withCount('assets');
|
||||
->withCount('assets as assets_count');
|
||||
|
||||
|
||||
|
||||
if ($request->has('status')) {
|
||||
if ($request->filled('status')) {
|
||||
$assetmodels->onlyTrashed();
|
||||
}
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($assetmodels) && ($request->get('offset') > $assetmodels->count())) ? $assetmodels->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at';
|
||||
|
||||
@@ -100,7 +119,7 @@ class AssetModelsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$assetmodel = AssetModel::withCount('assets')->findOrFail($id);
|
||||
$assetmodel = AssetModel::withCount('assets as assets_count')->findOrFail($id);
|
||||
return (new AssetModelsTransformer)->transformAssetModel($assetmodel);
|
||||
}
|
||||
|
||||
@@ -131,13 +150,13 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', AssetModel::class);
|
||||
$this->authorize('update', AssetModel::class);
|
||||
$assetmodel = AssetModel::findOrFail($id);
|
||||
$assetmodel->fill($request->all());
|
||||
$assetmodel->fieldset_id = $request->get("custom_fieldset_id");
|
||||
|
||||
if ($assetmodel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/assetmodels/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
|
||||
@@ -165,12 +184,12 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$assetmodel->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
$assetmodel->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/assetmodels/message.delete.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
@@ -196,7 +215,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels = $assetmodels->SearchByManufacturerOrCat($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index(Request $request, $audit = null)
|
||||
{
|
||||
|
||||
$this->authorize('index', Asset::class);
|
||||
@@ -77,12 +77,15 @@ class AssetsController extends Controller
|
||||
'last_audit_date',
|
||||
'next_audit_date',
|
||||
'warranty_months',
|
||||
'checkout_counter',
|
||||
'checkin_counter',
|
||||
'requests_counter',
|
||||
];
|
||||
|
||||
$filter = array();
|
||||
|
||||
if ($request->has('filter')) {
|
||||
$filter = json_decode($request->input('filter'));
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
}
|
||||
|
||||
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
|
||||
@@ -92,60 +95,85 @@ class AssetsController extends Controller
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
|
||||
|
||||
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
|
||||
// These are used by the API to query against specific ID numbers.
|
||||
// They are also used by the individual searches on detail pages like
|
||||
// locations, etc.
|
||||
if ($request->has('status_id')) {
|
||||
if ($request->filled('status_id')) {
|
||||
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
||||
}
|
||||
|
||||
if ($request->has('model_id')) {
|
||||
if ($request->input('requestable')=='true') {
|
||||
$assets->where('assets.requestable', '=', '1');
|
||||
}
|
||||
|
||||
if ($request->filled('model_id')) {
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
}
|
||||
|
||||
if ($request->has('category_id')) {
|
||||
if ($request->filled('category_id')) {
|
||||
$assets->InCategory($request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
if ($request->filled('location_id')) {
|
||||
$assets->where('assets.location_id', '=', $request->input('location_id'));
|
||||
// dd($assets->toSql());
|
||||
}
|
||||
|
||||
if ($request->has('supplier_id')) {
|
||||
if ($request->filled('rtd_location_id')) {
|
||||
$assets->where('assets.rtd_location_id', '=', $request->input('rtd_location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('supplier_id')) {
|
||||
$assets->where('assets.supplier_id', '=', $request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if (($request->has('assigned_to')) && ($request->has('assigned_type'))) {
|
||||
if (($request->filled('assigned_to')) && ($request->filled('assigned_type'))) {
|
||||
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
|
||||
->where('assets.assigned_type', '=', $request->input('assigned_type'));
|
||||
->where('assets.assigned_type', '=', $request->input('assigned_type'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$assets->where('assets.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$assets->ByManufacturer($request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->has('depreciation_id')) {
|
||||
if ($request->filled('depreciation_id')) {
|
||||
$assets->ByDepreciationId($request->input('depreciation_id'));
|
||||
}
|
||||
|
||||
$request->has('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
|
||||
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
|
||||
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($assets) && ($request->get('offset') > $assets->count())) ? $assets->count() : $request->get('offset', 0);
|
||||
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
// This is used by the audit reporting routes
|
||||
if (Gate::allows('audit', Asset::class)) {
|
||||
|
||||
switch ($audit) {
|
||||
case 'due':
|
||||
$assets->DueOrOverdueForAudit($settings);
|
||||
break;
|
||||
case 'overdue':
|
||||
$assets->overdueForAudit($settings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This is used by the sidenav, mostly
|
||||
|
||||
// We switched from using query scopes here because of a Laravel bug
|
||||
@@ -165,12 +193,12 @@ class AssetsController extends Controller
|
||||
break;
|
||||
case 'RTD':
|
||||
$assets->whereNull('assets.assigned_to')
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
break;
|
||||
case 'Undeployable':
|
||||
$assets->Undeployable();
|
||||
@@ -186,11 +214,11 @@ class AssetsController extends Controller
|
||||
case 'Requestable':
|
||||
$assets->where('assets.requestable', '=', 1)
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
|
||||
break;
|
||||
case 'Deployed':
|
||||
@@ -199,14 +227,14 @@ class AssetsController extends Controller
|
||||
break;
|
||||
default:
|
||||
|
||||
if ((!$request->has('status_id')) && ($settings->show_archived_in_list!='1')) {
|
||||
if ((!$request->filled('status_id')) && ($settings->show_archived_in_list!='1')) {
|
||||
// terrible workaround for complex-query Laravel bug in fulltext
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
|
||||
// If there is a status ID, don't take show_archived_in_list into consideration
|
||||
|
||||
// If there is a status ID, don't take show_archived_in_list into consideration
|
||||
} else {
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id");
|
||||
@@ -215,9 +243,10 @@ class AssetsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
if (count($filter) > 0) {
|
||||
|
||||
if ((!is_null($filter)) && (count($filter)) > 0) {
|
||||
$assets->ByFilter($filter);
|
||||
} elseif ($request->has('search')) {
|
||||
} elseif ($request->filled('search')) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
@@ -230,8 +259,8 @@ class AssetsController extends Controller
|
||||
// This handles all of the pivot sorting (versus the assets.* fields
|
||||
// in the allowed_columns array)
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||
|
||||
|
||||
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
@@ -274,7 +303,46 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns JSON with information about an asset (by tag) for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param string $tag
|
||||
* @since [v4.2.1]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showByTag($tag)
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->where('asset_tag',$tag)->first()) {
|
||||
$this->authorize('view', $asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON with information about an asset (by serial) for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param string $serial
|
||||
* @since [v4.2.1]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showBySerial($serial)
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
if ($assets = Asset::with('assetstatus')->with('assignedTo')
|
||||
->withTrashed()->where('serial',$serial)->get()) {
|
||||
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON with information about an asset for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -284,11 +352,12 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->findOrFail($id)) {
|
||||
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as userRequests_count')->findOrFail($id)) {
|
||||
$this->authorize('view', $asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -311,10 +380,13 @@ class AssetsController extends Controller
|
||||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id'
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived());
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(),'company_id', 'assets');
|
||||
|
||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
}
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$assets = $assets->AssignedSearch($request->input('search'));
|
||||
}
|
||||
|
||||
@@ -333,7 +405,7 @@ class AssetsController extends Controller
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType()=='pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
}
|
||||
@@ -385,9 +457,15 @@ class AssetsController extends Controller
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
$model = AssetModel::find($request->get('model_id'));
|
||||
if ($model->fieldset) {
|
||||
if (($model) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug(), null));
|
||||
if ($field->field_encrypted=='1') {
|
||||
if (Gate::allows('admin')) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
|
||||
}
|
||||
} else {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,52 +498,30 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
($request->has('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : '';
|
||||
($request->has('name')) ?
|
||||
$asset->name = $request->get('name') : '';
|
||||
($request->has('serial')) ?
|
||||
$asset->serial = $request->get('serial') : '';
|
||||
($request->has('model_id')) ?
|
||||
$asset->model_id = $request->get('model_id') : '';
|
||||
($request->has('order_number')) ?
|
||||
$asset->order_number = $request->get('order_number') : '';
|
||||
($request->has('notes')) ?
|
||||
$asset->notes = $request->get('notes') : '';
|
||||
($request->has('asset_tag')) ?
|
||||
$asset->asset_tag = $request->get('asset_tag') : '';
|
||||
($request->has('archived')) ?
|
||||
$asset->archived = $request->get('archived') : '';
|
||||
($request->has('status_id')) ?
|
||||
$asset->status_id = $request->get('status_id') : '';
|
||||
($request->has('warranty_months')) ?
|
||||
$asset->warranty_months = $request->get('warranty_months') : '';
|
||||
($request->has('purchase_cost')) ?
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost')) : '';
|
||||
($request->has('purchase_date')) ?
|
||||
$asset->purchase_date = $request->get('purchase_date') : '';
|
||||
($request->has('assigned_to')) ?
|
||||
$asset->assigned_to = $request->get('assigned_to') : '';
|
||||
($request->has('supplier_id')) ?
|
||||
$asset->supplier_id = $request->get('supplier_id') : '';
|
||||
($request->has('requestable')) ?
|
||||
$asset->requestable = $request->get('requestable') : '';
|
||||
($request->has('rtd_location_id')) ?
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id') : '';
|
||||
($request->has('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : '';
|
||||
($request->has('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
|
||||
$asset->fill($request->all());
|
||||
|
||||
($request->filled('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
|
||||
($request->filled('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : null;
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
// Update custom fields
|
||||
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($request->has($field->convertUnicodeDbSlug())) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug()));
|
||||
if ($field->field_encrypted=='1') {
|
||||
if (Gate::allows('admin')) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
|
||||
}
|
||||
} else {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,11 +529,15 @@ class AssetsController extends Controller
|
||||
|
||||
if ($asset->save()) {
|
||||
|
||||
if (($request->has('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->has('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->has('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
|
||||
Asset::where('assigned_type', '\\App\\Models\\Asset')->where('assigned_to', $id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
@@ -560,16 +620,14 @@ class AssetsController extends Controller
|
||||
$target = Asset::where('id','!=',$asset_id)->find(request('assigned_asset'));
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
// Override with the asset's location_id if it has one
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
}
|
||||
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_asset');
|
||||
$error_payload['target_type'] = 'asset';
|
||||
|
||||
} elseif (request('checkout_to_type')=='user') {
|
||||
// Fetch the target and set the asset's new location_id
|
||||
$target = User::find(request('assigned_user'));
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_user');
|
||||
$error_payload['target_type'] = 'user';
|
||||
}
|
||||
@@ -586,21 +644,22 @@ class AssetsController extends Controller
|
||||
$expected_checkin = request('expected_checkin', null);
|
||||
$note = request('note', null);
|
||||
$asset_name = request('name', null);
|
||||
|
||||
|
||||
// Set the location ID to the RTD location id if there is one
|
||||
if ($asset->rtd_location_id!='') {
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
}
|
||||
// Wait, why are we doing this? This overrides the stuff we set further up, which makes no sense.
|
||||
// TODO: Follow up here. WTF. Commented out for now.
|
||||
|
||||
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
|
||||
// $asset->location_id = $target->rtd_location_id;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')))->withErrors($asset->getErrors());
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
|
||||
}
|
||||
|
||||
|
||||
@@ -629,42 +688,23 @@ class AssetsController extends Controller
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
$asset->name = e(Input::get('name'));
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$asset->name = $request->input('name');
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
if ($request->filled('location_id')) {
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if (Input::has('status_id')) {
|
||||
$asset->status_id = e(Input::get('status_id'));
|
||||
$asset->status_id = Input::get('status_id');
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($asset->save()) {
|
||||
$logaction = $asset->logCheckin($target, e(request('note')));
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
|
||||
$data['item_name'] = $asset->present()->name();
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = $asset->asset_tag;
|
||||
$data['item_serial'] = $asset->serial;
|
||||
$data['note'] = $logaction->note;
|
||||
$data['manufacturer_name'] = $asset->model->manufacturer->name;
|
||||
$data['model_name'] = $asset->model->name;
|
||||
$data['model_number'] = $asset->model->model_number;
|
||||
|
||||
if ((($asset->checkin_email()=='1')) && (isset($user)) && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.checkin-asset', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Confirm_Asset_Checkin'));
|
||||
});
|
||||
}
|
||||
|
||||
$asset->logCheckin($target, e(request('note')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
@@ -720,5 +760,51 @@ class AssetsController extends Controller
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON listing of all requestable assets
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function requestable(Request $request)
|
||||
{
|
||||
$this->authorize('viewRequestable', Asset::class);
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$assets->TextSearch($request->input('search'));
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
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;
|
||||
default:
|
||||
$assets->orderBy('assets.created_at', $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,22 @@ class CategoriesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count', 'image'];
|
||||
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count','licenses_count', 'image'];
|
||||
|
||||
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email','image'])
|
||||
->withCount('assets', 'accessories', 'consumables', 'components');
|
||||
->withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count','licenses as licenses_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$categories = $categories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($categories) && ($request->get('offset') > $categories->count())) ? $categories->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
|
||||
$categories->orderBy($sort, $order);
|
||||
@@ -92,7 +97,7 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Category::class);
|
||||
$this->authorize('update', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
$category->fill($request->all());
|
||||
|
||||
@@ -148,7 +153,7 @@ class CategoriesController extends Controller
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$categories = $categories->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,19 @@ class CompaniesController extends Controller
|
||||
'components_count',
|
||||
];
|
||||
|
||||
$companies = Company::withCount('assets','licenses','accessories','consumables','components','users');
|
||||
$companies = Company::withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count','components as components_count','users as users_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$companies->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($companies) && ($request->get('offset') > $companies->count())) ? $companies->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$companies->orderBy($sort, $order);
|
||||
@@ -104,7 +109,7 @@ class CompaniesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Company::class);
|
||||
$this->authorize('update', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$company->fill($request->all());
|
||||
|
||||
@@ -168,7 +173,7 @@ class CompaniesController extends Controller
|
||||
'companies.image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
|
||||
@@ -24,19 +24,31 @@ class ComponentsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')->whereNull('components.deleted_at')
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')
|
||||
->with('company', 'location', 'category'));
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$components = $components->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$components->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
if ($request->filled('category_id')) {
|
||||
$components->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$components->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location','image'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -93,13 +105,11 @@ class ComponentsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
$component = Component::find($id);
|
||||
$component = Component::findOrFail($id);
|
||||
|
||||
if ($component) {
|
||||
return (new ComponentsTransformer)->transformComponent($component);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist')));
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +124,7 @@ class ComponentsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Component::class);
|
||||
$this->authorize('update', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
$component->fill($request->all());
|
||||
|
||||
|
||||
@@ -24,25 +24,33 @@ class ConsumablesController extends Controller
|
||||
$this->authorize('index', Consumable::class);
|
||||
$consumables = Company::scopeCompanyables(
|
||||
Consumable::select('consumables.*')
|
||||
->whereNull('consumables.deleted_at')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
||||
);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$consumables->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
if ($request->filled('category_id')) {
|
||||
$consumables->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$consumables->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($consumables) && ($request->get('offset') > $consumables->count())) ? $consumables->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty','image'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
@@ -121,7 +129,7 @@ class ConsumablesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Consumable::class);
|
||||
$this->authorize('update', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
$consumable->fill($request->all());
|
||||
|
||||
@@ -162,7 +170,7 @@ class ConsumablesController extends Controller
|
||||
{
|
||||
$consumable = Consumable::with(array('consumableAssignments'=>
|
||||
function ($query) {
|
||||
$query->orderBy('created_at', 'DESC');
|
||||
$query->orderBy($query->getModel()->getTable().'.created_at', 'DESC');
|
||||
},
|
||||
'consumableAssignments.admin'=> function ($query) {
|
||||
},
|
||||
@@ -179,7 +187,7 @@ class ConsumablesController extends Controller
|
||||
foreach ($consumable->consumableAssignments as $consumable_assignment) {
|
||||
$rows[] = [
|
||||
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
|
||||
'created_at' => ($consumable_assignment->created_at->format('Y-m-d H:i:s')=='-0001-11-30 00:00:00') ? '' : $consumable_assignment->created_at->format('Y-m-d H:i:s'),
|
||||
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
|
||||
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ class CustomFieldsController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomFields::class);
|
||||
$this->authorize('index', CustomField::class);
|
||||
$fields = CustomField::get();
|
||||
|
||||
$total = count($fields);
|
||||
return (new CustomFieldsTransformer)->transformCustomFields($fields, $total);
|
||||
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +38,7 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('show', CustomField::class);
|
||||
$this->authorize('view', CustomField::class);
|
||||
if ($field = CustomField::find($id)) {
|
||||
return (new CustomFieldsTransformer)->transformCustomField($field);
|
||||
}
|
||||
@@ -59,9 +57,15 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', CustomField::class);
|
||||
$this->authorize('update', CustomField::class);
|
||||
$field = CustomField::findOrFail($id);
|
||||
$data = $request->all();
|
||||
|
||||
/**
|
||||
* Updated values for the field,
|
||||
* without the "field_encrypted" flag, preventing the change of encryption status
|
||||
* @var array
|
||||
*/
|
||||
$data = $request->except(['field_encrypted']);
|
||||
|
||||
$validator = Validator::make($data, $field->validationRules());
|
||||
if ($validator->fails()) {
|
||||
@@ -108,6 +112,9 @@ class CustomFieldsController extends Controller
|
||||
public function postReorder(Request $request, $id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('update', $fieldset);
|
||||
|
||||
$fields = array();
|
||||
$order_array = array();
|
||||
|
||||
@@ -127,7 +134,8 @@ class CustomFieldsController extends Controller
|
||||
|
||||
public function associate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
@@ -144,7 +152,8 @@ class CustomFieldsController extends Controller
|
||||
|
||||
public function disassociate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
@@ -169,6 +178,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->fieldset->count() >0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Field is in use.'));
|
||||
}
|
||||
|
||||
@@ -43,10 +43,8 @@ class CustomFieldsetsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomFieldset::class);
|
||||
$fieldsets = CustomFieldset::withCount(['fields', 'models'])->get();
|
||||
|
||||
$total = count($fieldsets);
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $total);
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('show', CustomFieldset::class);
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
if ($fieldset = CustomFieldset::find($id)) {
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldset($fieldset);
|
||||
}
|
||||
@@ -81,7 +79,7 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
@@ -156,8 +154,26 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
$set = CustomFieldset::findOrFail($id);
|
||||
$fields = $set->fields->get();
|
||||
$fields = $set->fields;
|
||||
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of fields belonging to a fieldset with the
|
||||
* default values for a given model
|
||||
*
|
||||
* @param $modelId
|
||||
* @param $fieldsetId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function fieldsWithDefaultValues($fieldsetId, $modelId)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
|
||||
$set = CustomFieldset::findOrFail($fieldsetId);
|
||||
|
||||
$fields = $set->fields;
|
||||
|
||||
return (new CustomFieldsTransformer)->transformCustomFieldsWithDefaultValues($fields, $modelId, $fields->count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,35 +15,51 @@ class DepartmentsController extends Controller
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @author [Godfrey Martinez] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$allowed_columns = ['id','name','image'];
|
||||
$allowed_columns = ['id','name','image','users_count'];
|
||||
|
||||
$departments = Department::select([
|
||||
'id',
|
||||
'name',
|
||||
'location_id',
|
||||
'company_id',
|
||||
'manager_id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'image'
|
||||
])->with('users')->with('location')->with('manager')->with('company')->withCount('users');
|
||||
'departments.id',
|
||||
'departments.name',
|
||||
'departments.location_id',
|
||||
'departments.company_id',
|
||||
'departments.manager_id',
|
||||
'departments.created_at',
|
||||
'departments.updated_at',
|
||||
'departments.image'
|
||||
])->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($departments) && ($request->get('offset') > $departments->count())) ? $departments->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$departments->orderBy($sort, $order);
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'location':
|
||||
$departments->OrderLocation($order);
|
||||
break;
|
||||
case 'manager':
|
||||
$departments->OrderManager($order);
|
||||
break;
|
||||
default:
|
||||
$departments->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$total = $departments->count();
|
||||
$departments = $departments->skip($offset)->take($limit)->get();
|
||||
@@ -65,7 +81,7 @@ class DepartmentsController extends Controller
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department->user_id = Auth::user()->id;
|
||||
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.create.success')));
|
||||
@@ -103,6 +119,8 @@ class DepartmentsController extends Controller
|
||||
{
|
||||
$department = Department::findOrFail($id);
|
||||
|
||||
$this->authorize('delete', $department);
|
||||
|
||||
if ($department->users->count() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/departments/message.assoc_users')));
|
||||
}
|
||||
@@ -129,7 +147,7 @@ class DepartmentsController extends Controller
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
@@ -145,5 +163,28 @@ class DepartmentsController extends Controller
|
||||
return (new SelectlistTransformer)->transformSelectlist($departments);
|
||||
|
||||
}
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', Department::class);
|
||||
$departments = Department::findOrFail($id);
|
||||
$departments->fill($request->all());
|
||||
|
||||
if ($departments->save()) {
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer())->transformdepartment($departments), trans('admin/departments/message.update.success')));
|
||||
}
|
||||
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('error', null, $departments->getErrors()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,12 +24,17 @@ class DepreciationsController extends Controller
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($depreciations) && ($request->get('offset') > $depreciations->count())) ? $depreciations->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$depreciations->orderBy($sort, $order);
|
||||
@@ -88,7 +93,7 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Depreciation::class);
|
||||
$this->authorize('update', Depreciation::class);
|
||||
$depreciation = Depreciation::findOrFail($id);
|
||||
$depreciation->fill($request->all());
|
||||
|
||||
|
||||
@@ -22,14 +22,19 @@ class GroupsController extends Controller
|
||||
$this->authorize('view', Group::class);
|
||||
$allowed_columns = ['id','name','created_at', 'users_count'];
|
||||
|
||||
$groups = Group::select('id','name','permissions','created_at','updated_at')->withCount('users');
|
||||
$groups = Group::select('id','name','permissions','created_at','updated_at')->withCount('users as users_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($groups) && ($request->get('offset') > $groups->count())) ? $groups->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$groups->orderBy($sort, $order);
|
||||
@@ -88,7 +93,7 @@ class GroupsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Group::class);
|
||||
$this->authorize('update', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
$group->fill($request->all());
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Session;
|
||||
use League\Csv\Reader;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Artisan;
|
||||
use App\Models\Asset;
|
||||
|
||||
class ImportController extends Controller
|
||||
{
|
||||
@@ -24,7 +25,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
return (new ImportsTransformer)->transformImports($imports);
|
||||
|
||||
@@ -38,10 +39,8 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
//
|
||||
if (!Company::isCurrentUserAuthorized()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('general.insufficient_permissions'));
|
||||
} elseif (!config('app.lock_passwords')) {
|
||||
$this->authorize('import');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$files = Input::file('files');
|
||||
$path = config('app.private_uploads').'/imports';
|
||||
$results = [];
|
||||
@@ -57,6 +56,35 @@ class ImportController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
|
||||
}
|
||||
|
||||
//TODO: is there a lighter way to do this?
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
|
||||
$import->header_row = $reader->fetchOne(0);
|
||||
|
||||
//duplicate headers check
|
||||
$duplicate_headers = [];
|
||||
|
||||
for($i = 0; $i<count($import->header_row); $i++) {
|
||||
$header = $import->header_row[$i];
|
||||
if(in_array($header, $import->header_row)) {
|
||||
$found_at = array_search($header, $import->header_row);
|
||||
if($i > $found_at) {
|
||||
//avoid reporting duplicates twice, e.g. "1 is same as 17! 17 is same as 1!!!"
|
||||
//as well as "1 is same as 1!!!" (which is always true)
|
||||
//has to be > because otherwise the first result of array_search will always be $i itself(!)
|
||||
array_push($duplicate_headers,"Duplicate header '$header' detected, first at column: ".($found_at+1).", repeats at column: ".($i+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(count($duplicate_headers) > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error',null, implode("; ",$duplicate_headers)), 500); //should this be '4xx'?
|
||||
}
|
||||
|
||||
// Grab the first row to display via ajax as the user picks fields
|
||||
$import->first_row = $reader->fetchOne(1);
|
||||
|
||||
$date = date('Y-m-d-his');
|
||||
$fixed_filename = str_slug($file->getClientOriginalName());
|
||||
try {
|
||||
@@ -71,11 +99,6 @@ class ImportController extends Controller
|
||||
$file_name = date('Y-m-d-his').'-'.$fixed_filename;
|
||||
$import->file_path = $file_name;
|
||||
$import->filesize = filesize($path.'/'.$file_name);
|
||||
//TODO: is there a lighter way to do this?
|
||||
$reader = Reader::createFromPath("{$path}/{$file_name}");
|
||||
$import->header_row = $reader->fetchOne(0);
|
||||
// Grab the first row to display via ajax as the user picks fields
|
||||
$import->first_row = $reader->fetchOne(1);
|
||||
$import->save();
|
||||
$results[] = $import;
|
||||
}
|
||||
@@ -94,9 +117,16 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function process(ItemImportRequest $request, $import_id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
|
||||
if ($request->has('run-backup')) {
|
||||
\Log::debug('Backup manually requested via importer');
|
||||
Artisan::call('backup:run');
|
||||
} else {
|
||||
\Log::debug('NO BACKUP requested via importer');
|
||||
}
|
||||
|
||||
$errors = $request->import(Import::find($import_id));
|
||||
$redirectTo = "hardware.index";
|
||||
switch ($request->get('import-type')) {
|
||||
@@ -137,7 +167,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function destroy($import_id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
$import = Import::find($import_id);
|
||||
try {
|
||||
unlink(config('app.private_uploads').'/imports/'.$import->file_path);
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\LicenseSeatsTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Models\Company;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LicensesController extends Controller
|
||||
{
|
||||
@@ -23,61 +25,70 @@ class LicensesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier')->withCount('freeSeats'));
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats as free_seats_count'));
|
||||
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$licenses->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('name')) {
|
||||
if ($request->filled('name')) {
|
||||
$licenses->where('licenses.name','=',$request->input('name'));
|
||||
}
|
||||
|
||||
if ($request->has('product_key')) {
|
||||
if ($request->filled('product_key')) {
|
||||
$licenses->where('licenses.serial','=',$request->input('product_key'));
|
||||
}
|
||||
|
||||
if ($request->has('order_number')) {
|
||||
if ($request->filled('order_number')) {
|
||||
$licenses->where('order_number','=',$request->input('order_number'));
|
||||
}
|
||||
|
||||
if ($request->has('purchase_order')) {
|
||||
if ($request->filled('purchase_order')) {
|
||||
$licenses->where('purchase_order','=',$request->input('purchase_order'));
|
||||
}
|
||||
|
||||
if ($request->has('license_name')) {
|
||||
if ($request->filled('license_name')) {
|
||||
$licenses->where('license_name','=',$request->input('license_name'));
|
||||
}
|
||||
|
||||
if ($request->has('license_email')) {
|
||||
if ($request->filled('license_email')) {
|
||||
$licenses->where('license_email','=',$request->input('license_email'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$licenses->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->has('supplier_id')) {
|
||||
if ($request->filled('supplier_id')) {
|
||||
$licenses->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->has('depreciation_id')) {
|
||||
if ($request->filled('category_id')) {
|
||||
$licenses->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation_id')) {
|
||||
$licenses->where('depreciation_id','=',$request->input('depreciation_id'));
|
||||
}
|
||||
|
||||
if ($request->has('supplier_id')) {
|
||||
if ($request->filled('supplier_id')) {
|
||||
$licenses->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($licenses) && ($request->get('offset') > $licenses->count())) ? $licenses->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
@@ -88,11 +99,14 @@ class LicensesController extends Controller
|
||||
case 'supplier':
|
||||
$licenses = $licenses->leftJoin('suppliers', 'licenses.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
|
||||
break;
|
||||
case 'category':
|
||||
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
|
||||
break;
|
||||
case 'company':
|
||||
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','license_name','license_email','free_seats_count','seats'];
|
||||
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$licenses = $licenses->orderBy($sort, $order);
|
||||
break;
|
||||
@@ -121,6 +135,14 @@ class LicensesController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
$this->authorize('create', License::class);
|
||||
$license = new License;
|
||||
$license->fill($request->all());
|
||||
|
||||
if($license->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $license->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,13 +154,10 @@ class LicensesController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$license = License::find($id);
|
||||
if (isset($license->id)) {
|
||||
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
|
||||
$this->authorize('view', $license);
|
||||
return (new LicensesTransformer)->transformLicense($license);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
|
||||
$this->authorize('view', License::class);
|
||||
$license = License::findOrFail($id);
|
||||
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
|
||||
return (new LicensesTransformer)->transformLicense($license);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +173,16 @@ class LicensesController extends Controller
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
$this->authorize('update', License::class);
|
||||
|
||||
$license = License::findOrFail($id);
|
||||
$license->fill($request->all());
|
||||
|
||||
if ($license->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $license, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
return Helper::formatStandardApiResponse('error', null, $license->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,6 +196,23 @@ class LicensesController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
$license = License::findOrFail($id);
|
||||
$this->authorize('delete', $license);
|
||||
|
||||
if($license->assigned_seats_count == 0) {
|
||||
// Delete the license and the associated license seats
|
||||
DB::table('license_seats')
|
||||
->where('id', $license->id)
|
||||
->update(array('assigned_to' => null,'asset_id' => null));
|
||||
|
||||
$licenseSeats = $license->licenseseats();
|
||||
$licenseSeats->delete();
|
||||
$license->delete();
|
||||
|
||||
// Redirect to the licenses management page
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/licenses/message.delete.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.assoc_users')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,13 +228,23 @@ class LicensesController extends Controller
|
||||
|
||||
if ($license = License::find($licenseId)) {
|
||||
|
||||
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::where('license_seats.license_id', $licenseId)
|
||||
->with('license', 'user', 'asset', 'user.department');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort')=='department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} else {
|
||||
$seats->orderBy('id', $order);
|
||||
}
|
||||
|
||||
$total = $seats->count();
|
||||
$offset = (($seats) && (request('offset') > $total)) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$seats = $seats->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($seats) {
|
||||
|
||||
@@ -8,6 +8,8 @@ use App\Helpers\Helper;
|
||||
use App\Models\Location;
|
||||
use App\Http\Transformers\LocationsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class LocationsController extends Controller
|
||||
{
|
||||
@@ -26,7 +28,7 @@ class LocationsController extends Controller
|
||||
'updated_at','manager_id','image',
|
||||
'assigned_assets_count','users_count','assets_count','currency'];
|
||||
|
||||
$locations = Location::with('parent', 'manager', 'childLocations')->select([
|
||||
$locations = Location::with('parent', 'manager', 'children')->select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.address',
|
||||
@@ -41,18 +43,22 @@ class LocationsController extends Controller
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency'
|
||||
])->withCount('assignedAssets')
|
||||
->withCount('assets')
|
||||
->withCount('users');
|
||||
])->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('users as users_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$locations = $locations->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($locations) && ($request->get('offset') > $locations->count())) ? $locations->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -60,6 +66,9 @@ class LocationsController extends Controller
|
||||
case 'parent':
|
||||
$locations->OrderParent($order);
|
||||
break;
|
||||
case 'manager':
|
||||
$locations->OrderManager($order);
|
||||
break;
|
||||
default:
|
||||
$locations->orderBy($sort, $order);
|
||||
break;
|
||||
@@ -103,7 +112,26 @@ class LocationsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
$location = Location::with('parent', 'manager', 'children')
|
||||
->select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.address',
|
||||
'locations.address2',
|
||||
'locations.city',
|
||||
'locations.state',
|
||||
'locations.zip',
|
||||
'locations.country',
|
||||
'locations.parent_id',
|
||||
'locations.manager_id',
|
||||
'locations.created_at',
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency'
|
||||
])
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('users as users_count')->findOrFail($id);
|
||||
return (new LocationsTransformer)->transformLocation($location);
|
||||
}
|
||||
|
||||
@@ -119,8 +147,15 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
|
||||
if ($request->input('parent_id') == $id) {
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'A location cannot be its own parent. Please select a different parent ID.'));
|
||||
}
|
||||
|
||||
|
||||
$location->fill($request->all());
|
||||
|
||||
if ($location->save()) {
|
||||
@@ -156,6 +191,27 @@ class LocationsController extends Controller
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* This is handled slightly differently as of ~4.7.8-pre, as
|
||||
* we have to do some recursive magic to get the hierarchy to display
|
||||
* properly when looking at the parent/child relationship in the
|
||||
* rich menus.
|
||||
*
|
||||
* This means we can't use the normal pagination that we use elsewhere
|
||||
* in our selectlists, since we have to get the full set before we can
|
||||
* determine which location is parent/child/grandchild, etc.
|
||||
*
|
||||
* This also means that hierarchy display gets a little funky when people
|
||||
* use the Select2 search functionality, but there's not much we can do about
|
||||
* that right now.
|
||||
*
|
||||
* As a result, instead of paginating as part of the query, we have to grab
|
||||
* the entire data set, and then invoke a paginator manually and pass that
|
||||
* through to the SelectListTransformer.
|
||||
*
|
||||
* Many thanks to @uberbrady for the help getting this working better.
|
||||
* Recursion still sucks, but I guess he doesn't have to get in the
|
||||
* sea... this time.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
@@ -167,25 +223,44 @@ class LocationsController extends Controller
|
||||
$locations = Location::select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.parent_id',
|
||||
'locations.image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
$page = 1;
|
||||
if ($request->filled('page')) {
|
||||
$page = $request->input('page');
|
||||
}
|
||||
|
||||
$locations = $locations->orderBy('name', 'ASC')->paginate(50);
|
||||
if ($request->filled('search')) {
|
||||
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->input('search').'%');
|
||||
}
|
||||
|
||||
$locations = $locations->orderBy('name', 'ASC')->get();
|
||||
|
||||
$locations_with_children = [];
|
||||
|
||||
// Loop through and set some custom properties for the transformer to use.
|
||||
// This lets us have more flexibility in special cases like assets, where
|
||||
// they may not have a ->name value but we want to display something anyway
|
||||
foreach ($locations as $location) {
|
||||
$location->use_text = $location->name;
|
||||
$location->use_image = ($location->image) ? url('/').'/uploads/locations/'.$location->image : null;
|
||||
if (!array_key_exists($location->parent_id, $locations_with_children)) {
|
||||
$locations_with_children[$location->parent_id] = [];
|
||||
}
|
||||
$locations_with_children[$location->parent_id][] = $location;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($locations);
|
||||
if ($request->filled('search')) {
|
||||
$locations_formatted = $locations;
|
||||
} else {
|
||||
$location_options = Location::indenter($locations_with_children);
|
||||
$locations_formatted = new Collection($location_options);
|
||||
|
||||
}
|
||||
|
||||
$paginated_results = new LengthAwarePaginator($locations_formatted->forPage($page, 500), $locations_formatted->count(), 500, $page, []);
|
||||
|
||||
//return [];
|
||||
return (new SelectlistTransformer)->transformSelectlist($paginated_results);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,16 +25,25 @@ class ManufacturersController extends Controller
|
||||
$allowed_columns = ['id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count'];
|
||||
|
||||
$manufacturers = Manufacturer::select(
|
||||
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image')
|
||||
)->withCount('assets')->withCount('licenses')->withCount('consumables')->withCount('accessories');
|
||||
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', 'deleted_at')
|
||||
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count');
|
||||
|
||||
if ($request->input('deleted')=='true') {
|
||||
$manufacturers->onlyTrashed();
|
||||
}
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$manufacturers = $manufacturers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($manufacturers) && ($request->get('offset') > $manufacturers->count())) ? $manufacturers->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$manufacturers->orderBy($sort, $order);
|
||||
@@ -77,7 +86,7 @@ class ManufacturersController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer = Manufacturer::withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('consumables as consumables_count')->withCount('accessories as accessories_count')->findOrFail($id);
|
||||
return (new ManufacturersTransformer)->transformManufacturer($manufacturer);
|
||||
}
|
||||
|
||||
@@ -93,7 +102,7 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer->fill($request->all());
|
||||
|
||||
@@ -139,7 +148,7 @@ class ManufacturersController extends Controller
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
|
||||
49
app/Http/Controllers/Api/ProfileController.php
Normal file
49
app/Http/Controllers/Api/ProfileController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Auth;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of requested assets.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.3.0]
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function requestedAssets()
|
||||
{
|
||||
$checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
|
||||
|
||||
$results = [];
|
||||
$results['total'] = $checkoutRequests->count();
|
||||
|
||||
|
||||
foreach ($checkoutRequests as $checkoutRequest) {
|
||||
|
||||
// Make sure the asset and request still exist
|
||||
if ($checkoutRequest && $checkoutRequest->itemRequested()) {
|
||||
$results['rows'][] = [
|
||||
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
|
||||
'name' => $checkoutRequest->itemRequested()->present()->name(),
|
||||
'type' => $checkoutRequest->itemType(),
|
||||
'qty' => $checkoutRequest->quantity,
|
||||
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
|
||||
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,28 +18,32 @@ class ReportsController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target','location');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if (($request->has('target_type')) && ($request->has('target_id'))) {
|
||||
if (($request->filled('target_type')) && ($request->filled('target_id'))) {
|
||||
$actionlogs = $actionlogs->where('target_id','=',$request->input('target_id'))
|
||||
->where('target_type','=',"App\\Models\\".ucwords($request->input('target_type')));
|
||||
|
||||
}
|
||||
|
||||
if (($request->has('item_type')) && ($request->has('item_id'))) {
|
||||
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
|
||||
$actionlogs = $actionlogs->where('item_id','=',$request->input('item_id'))
|
||||
->where('item_type','=',"App\\Models\\".ucwords($request->input('item_type')));
|
||||
}
|
||||
|
||||
if ($request->has('action_type')) {
|
||||
if ($request->filled('action_type')) {
|
||||
$actionlogs = $actionlogs->where('action_type','=',$request->input('action_type'))->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
if ($request->filled('uploads')) {
|
||||
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'created_at',
|
||||
|
||||
@@ -8,6 +8,9 @@ use App\Models\Ldap;
|
||||
use Validator;
|
||||
use App\Models\Setting;
|
||||
use Mail;
|
||||
use App\Notifications\SlackTest;
|
||||
use Notification;
|
||||
use App\Notifications\MailTest;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
@@ -17,8 +20,8 @@ class SettingsController extends Controller
|
||||
{
|
||||
|
||||
if (Setting::getSettings()->ldap_enabled!='1') {
|
||||
\Log::debug('LDAP is not enabled cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
|
||||
\Log::debug('LDAP is not enabled so cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, so we cannot test LDAP connections.'], 400);
|
||||
}
|
||||
|
||||
\Log::debug('Preparing to test LDAP connection');
|
||||
@@ -30,13 +33,13 @@ class SettingsController extends Controller
|
||||
Ldap::bindAdminToLdap($connection);
|
||||
return response()->json(['message' => 'It worked!'], 200);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Bind failed');
|
||||
\Log::debug('LDAP connected but Bind failed. Please check your LDAP settings and try again.');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
//return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed');
|
||||
return response()->json(['message' => $e->getMessage()], 600);
|
||||
\Log::info('LDAP connection failed but we cannot debug it any further on our end.');
|
||||
return response()->json(['message' => 'The LDAP connection failed but we cannot debug it any further on our end. The error from the server is: '.$e->getMessage()], 500);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +99,29 @@ class SettingsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function slacktest()
|
||||
{
|
||||
|
||||
if ($settings = Setting::getSettings()->slack_channel=='') {
|
||||
\Log::debug('Slack is not enabled. Cannot test.');
|
||||
return response()->json(['message' => 'Slack is not enabled, cannot test.'], 400);
|
||||
}
|
||||
|
||||
\Log::debug('Preparing to test slack connection');
|
||||
|
||||
try {
|
||||
Notification::send($settings = Setting::getSettings(), new SlackTest());
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Slack connection failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the email configuration
|
||||
*
|
||||
@@ -107,11 +133,7 @@ class SettingsController extends Controller
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
try {
|
||||
Mail::send('emails.test', [], function ($m) {
|
||||
$m->to(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.test_email'));
|
||||
});
|
||||
Notification::send(Setting::first(), new MailTest());
|
||||
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
|
||||
} catch (Exception $e) {
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
|
||||
@@ -22,16 +22,21 @@ class StatuslabelsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$allowed_columns = ['id','name','created_at', 'assets_count'];
|
||||
$allowed_columns = ['id','name','created_at', 'assets_count','color','default_label'];
|
||||
|
||||
$statuslabels = Statuslabel::withCount('assets');
|
||||
$statuslabels = Statuslabel::withCount('assets as assets_count');
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$statuslabels = $statuslabels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$statuslabels->orderBy($sort, $order);
|
||||
@@ -55,8 +60,8 @@ class StatuslabelsController extends Controller
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
$request->except('deployable', 'pending','archived');
|
||||
|
||||
if (!$request->has('type')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]));
|
||||
if (!$request->filled('type')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]),500);
|
||||
}
|
||||
|
||||
$statuslabel = new Statuslabel;
|
||||
@@ -101,12 +106,12 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Statuslabel::class);
|
||||
$this->authorize('update', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
|
||||
$request->except('deployable', 'pending','archived');
|
||||
|
||||
if (!$request->has('type')) {
|
||||
if (!$request->filled('type')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Status label type is required.'));
|
||||
}
|
||||
|
||||
@@ -160,8 +165,9 @@ class StatuslabelsController extends Controller
|
||||
|
||||
public function getAssetCountByStatuslabel()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets')->get();
|
||||
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets as assets_count')->get();
|
||||
|
||||
$labels=[];
|
||||
$points=[];
|
||||
@@ -237,6 +243,8 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function checkIfDeployable($id) {
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
if ($statuslabel->getStatuslabelType()=='deployable') {
|
||||
return '1';
|
||||
}
|
||||
|
||||
@@ -25,16 +25,21 @@ class SuppliersController extends Controller
|
||||
$allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count'];
|
||||
|
||||
$suppliers = Supplier::select(
|
||||
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image')
|
||||
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
|
||||
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
|
||||
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
|
||||
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$suppliers = $suppliers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($suppliers) && ($request->get('offset') > $suppliers->count())) ? $suppliers->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$suppliers->orderBy($sort, $order);
|
||||
@@ -93,7 +98,7 @@ class SuppliersController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Supplier::class);
|
||||
$this->authorize('update', Supplier::class);
|
||||
$supplier = Supplier::findOrFail($id);
|
||||
$supplier->fill($request->all());
|
||||
|
||||
@@ -115,7 +120,7 @@ class SuppliersController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets', 'licenses')->findOrFail($id);
|
||||
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances as asset_maintenances_count','assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
|
||||
$this->authorize('delete', $supplier);
|
||||
|
||||
|
||||
@@ -153,7 +158,7 @@ class SuppliersController extends Controller
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use Auth;
|
||||
|
||||
class UsersController extends Controller
|
||||
{
|
||||
@@ -28,63 +31,72 @@ class UsersController extends Controller
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$users = User::select([
|
||||
'users.id',
|
||||
'users.employee_num',
|
||||
'users.two_factor_enrolled',
|
||||
'users.jobtitle',
|
||||
'users.email',
|
||||
'users.phone',
|
||||
'users.activated',
|
||||
'users.address',
|
||||
'users.avatar',
|
||||
'users.city',
|
||||
'users.state',
|
||||
'users.country',
|
||||
'users.zip',
|
||||
'users.username',
|
||||
'users.location_id',
|
||||
'users.manager_id',
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.created_at',
|
||||
'users.notes',
|
||||
'users.company_id',
|
||||
'users.last_login',
|
||||
'users.country',
|
||||
'users.created_at',
|
||||
'users.deleted_at',
|
||||
'users.department_id',
|
||||
'users.activated',
|
||||
'users.avatar',
|
||||
'users.email',
|
||||
'users.employee_num',
|
||||
'users.first_name',
|
||||
'users.id',
|
||||
'users.jobtitle',
|
||||
'users.last_login',
|
||||
'users.last_name',
|
||||
'users.location_id',
|
||||
'users.manager_id',
|
||||
'users.notes',
|
||||
'users.permissions',
|
||||
'users.phone',
|
||||
'users.state',
|
||||
'users.two_factor_enrolled',
|
||||
'users.two_factor_optin',
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables')
|
||||
->withCount('assets','licenses','accessories','consumables');
|
||||
->withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count');
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
|
||||
if (($request->has('deleted')) && ($request->input('deleted')=='true')) {
|
||||
if (($request->filled('deleted')) && ($request->input('deleted')=='true')) {
|
||||
$users = $users->GetDeleted();
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$users = $users->where('users.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
if ($request->filled('location_id')) {
|
||||
$users = $users->where('users.location_id', '=', $request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->has('group_id')) {
|
||||
|
||||
if ($request->filled('group_id')) {
|
||||
$users = $users->ByGroup($request->get('group_id'));
|
||||
}
|
||||
|
||||
if ($request->has('department_id')) {
|
||||
if ($request->filled('department_id')) {
|
||||
$users = $users->where('users.department_id','=',$request->input('department_id'));
|
||||
}
|
||||
|
||||
if ($request->has('search')) {
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 20);
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($users) && ($request->get('offset') > $users->count())) ? $users->count() : $request->get('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'manager':
|
||||
@@ -96,6 +108,9 @@ class UsersController extends Controller
|
||||
case 'department':
|
||||
$users = $users->OrderDepartment($order);
|
||||
break;
|
||||
case 'company':
|
||||
$users = $users->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns =
|
||||
[
|
||||
@@ -103,7 +118,7 @@ class UsersController extends Controller
|
||||
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
|
||||
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
|
||||
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
|
||||
'country', 'zip'
|
||||
'country', 'zip', 'id'
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
@@ -140,13 +155,12 @@ class UsersController extends Controller
|
||||
'users.avatar',
|
||||
'users.email',
|
||||
]
|
||||
);
|
||||
)->where('show_in_list', '=', '1');
|
||||
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
if ($request->has('search')) {
|
||||
$users = $users->where('first_name', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('last_name', 'LIKE', '%'.$request->get('search').'%')
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->SimpleNameSearch($request->get('search'))
|
||||
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
@@ -189,12 +203,32 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function store(SaveUserRequest $request)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = new User;
|
||||
$user->fill($request->all());
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
|
||||
if ($request->has('permissions')) {
|
||||
|
||||
$permissions_array = $request->input('permissions');
|
||||
|
||||
// Strip out the superuser permission if the API user isn't a superadmin
|
||||
if (!Auth::user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
}
|
||||
$user->permissions = $permissions_array;
|
||||
}
|
||||
|
||||
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
$user->password = bcrypt($request->get('password', $tmp_pass));
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
@@ -210,7 +244,7 @@ class UsersController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$user = User::withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count')->findOrFail($id);
|
||||
return (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
|
||||
@@ -226,7 +260,8 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function update(SaveUserRequest $request, $id)
|
||||
{
|
||||
$this->authorize('edit', User::class);
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
$user = User::findOrFail($id);
|
||||
$user->fill($request->all());
|
||||
|
||||
@@ -234,15 +269,48 @@ class UsersController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
|
||||
}
|
||||
|
||||
if ($request->has('password')) {
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
// We need to use has() instead of filled()
|
||||
// here because we need to overwrite permissions
|
||||
// if someone needs to null them out
|
||||
if ($request->has('permissions')) {
|
||||
|
||||
$permissions_array = $request->input('permissions');
|
||||
|
||||
// Strip out the superuser permission if the API user isn't a superadmin
|
||||
if (!Auth::user()->isSuperUser()) {
|
||||
unset($permissions_array['superuser']);
|
||||
}
|
||||
$user->permissions = $permissions_array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 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)]);
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
// Sync group memberships:
|
||||
// This was changed in Snipe-IT v4.6.x to 4.7, since we upgraded to Laravel 5.5
|
||||
// which changes the behavior of has vs filled.
|
||||
// The $request->has method will now return true even if the input value is an empty string or null.
|
||||
// A new $request->filled method has was added that provides the previous behavior of the has method.
|
||||
|
||||
// Check if the request has groups passed and has a value
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
// The groups field has been passed but it is null, so we should blank it out
|
||||
} elseif ($request->has('groups')) {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
|
||||
}
|
||||
|
||||
@@ -264,10 +332,23 @@ class UsersController extends Controller
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
|
||||
if ($user->assets()->count() > 0) {
|
||||
if (($user->assets) && ($user->assets->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete_has_assets')));
|
||||
}
|
||||
|
||||
if (($user->licenses) && ($user->licenses->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->licenses->count() . ' license(s) associated with them and cannot be deleted.'));
|
||||
}
|
||||
|
||||
if (($user->accessories) && ($user->accessories->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->accessories->count() . ' accessories associated with them.'));
|
||||
}
|
||||
|
||||
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'This user still has ' . $user->managedLocations()->count() . ' locations that they manage.'));
|
||||
}
|
||||
|
||||
|
||||
if ($user->delete()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
|
||||
}
|
||||
@@ -285,7 +366,83 @@ class UsersController extends Controller
|
||||
public function assets($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of accessories assigned to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.6.14]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function accessories($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessories = $user->accessories;
|
||||
return (new AccessoriesTransformer)->transformAccessories($accessories, $accessories->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of licenses assigned to a user.
|
||||
*
|
||||
* @author [N. Mathar] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function licenses($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('view', License::class);
|
||||
$user = User::where('id', $id)->withTrashed()->first();
|
||||
$licenses = $user->licenses()->get();
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the user's two-factor status
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function postTwoFactorReset(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
if ($request->filled('id')) {
|
||||
try {
|
||||
$user = User::find($request->get('id'));
|
||||
$user->two_factor_secret = null;
|
||||
$user->two_factor_enrolled = 0;
|
||||
$user->save();
|
||||
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
|
||||
}
|
||||
}
|
||||
return response()->json(['message' => 'No ID provided'], 500);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info on the current user.
|
||||
*
|
||||
* @author [Juan Font] [<juanfontalonso@gmail.com>]
|
||||
* @since [v4.4.2]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getCurrentUserInfo(Request $request)
|
||||
{
|
||||
return (new UsersTransformer)->transformUser($request->user());
|
||||
}
|
||||
}
|
||||
|
||||
104
app/Http/Controllers/AssetCheckinController.php
Normal file
104
app/Http/Controllers/AssetCheckinController.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetCheckinRequest;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AssetCheckinController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param string $backto
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function create($assetId, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckinRequest $request
|
||||
* @param int $assetId
|
||||
* @param null $backto
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetCheckinRequest $request, $assetId = null, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
if ($asset->assignedType() == Asset::USER) {
|
||||
$user = $asset->assignedTo;
|
||||
}
|
||||
if (is_null($target = $asset->assignedTo)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
|
||||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkout = null;
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->assigned_type = null;
|
||||
$asset->accepted = null;
|
||||
$asset->name = $request->get('name');
|
||||
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = e($request->get('status_id'));
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$asset->location_id = e($request->get('location_id'));
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($asset->save()) {
|
||||
$logaction = $asset->logCheckin($target, e(request('note')));
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
|
||||
$data['last_name'] = get_class($target) == User::class ? $target->last_name : '';
|
||||
$data['item_name'] = $asset->present()->name();
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = $asset->asset_tag;
|
||||
$data['item_serial'] = $asset->serial;
|
||||
$data['note'] = $logaction->note;
|
||||
$data['manufacturer_name'] = $asset->model->manufacturer->name;
|
||||
$data['model_name'] = $asset->model->name;
|
||||
$data['model_number'] = $asset->model->model_number;
|
||||
|
||||
if ((isset($user)) && ($backto =='user')) {
|
||||
return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
return redirect()->route("hardware.index")->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'));
|
||||
}
|
||||
}
|
||||
94
app/Http/Controllers/AssetCheckoutController.php
Normal file
94
app/Http/Controllers/AssetCheckoutController.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AssetCheckoutController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset out to a
|
||||
* user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function create($assetId)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find(e($assetId)))) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'));
|
||||
}
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check out an asset to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckoutRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetCheckoutRequest $request, $assetId)
|
||||
{
|
||||
try {
|
||||
// Check if the asset exists
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
} elseif (!$asset->availableForCheckout()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
}
|
||||
$this->authorize('checkout', $asset);
|
||||
$admin = Auth::user();
|
||||
|
||||
$target = $this->determineCheckoutTarget($asset);
|
||||
if ($asset->is($target)) {
|
||||
throw new CheckoutNotAllowed('You cannot check an asset out to itself.');
|
||||
}
|
||||
$asset = $this->updateAssetLocation($asset, $target);
|
||||
|
||||
$checkout_at = date("Y-m-d H:i:s");
|
||||
if (($request->filled('checkout_at')) && ($request->get('checkout_at')!= date("Y-m-d"))) {
|
||||
$checkout_at = $request->get('checkout_at');
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$expected_checkin = $request->get('expected_checkin');
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
} catch (CheckoutNotAllowed $e) {
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
129
app/Http/Controllers/AssetFilesController.php
Normal file
129
app/Http/Controllers/AssetFilesController.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Upload a file to the server.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetFileRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetFileRequest $request, $assetId = null)
|
||||
{
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$destinationPath = config('app.private_uploads').'/assets';
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
foreach ($request->file('file') as $file) {
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'hardware-'.$asset->id.'-'.str_random(8);
|
||||
$filename .= '-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
$asset->logUpload($filename, e($request->get('notes')));
|
||||
}
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function show($assetId = null, $fileId = null, $download = true)
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
if (!$log = Actionlog::find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
$file = $log->get_src('assets');
|
||||
|
||||
if ($log->action_type =='audit') {
|
||||
$file = $log->get_src('audits');
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return response('File '.$file.' not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
if ($download != 'true') {
|
||||
if ($contents = file_get_contents($file)) {
|
||||
return Response::make($contents)->header('Content-Type', mime_content_type($file));
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: "], 500);
|
||||
}
|
||||
return Response::download($file);
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function destroy($assetId = null, $fileId = null)
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
$this->authorize('update', $asset);
|
||||
$destinationPath = config('app.private_uploads').'/imports/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
return redirect()->back()->with('error', 'Could not find matching upload log.');
|
||||
|
||||
}
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class AssetMaintenancesController extends Controller
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
@@ -162,6 +162,9 @@ class AssetMaintenancesController extends Controller
|
||||
// Redirect to the improvement management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (!$assetMaintenance->asset) {
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', 'The asset associated with this maintenance does not exist.');
|
||||
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
@@ -90,72 +90,20 @@ class AssetModelsController extends Controller
|
||||
$model->fieldset_id = e($request->input('custom_fieldset'));
|
||||
}
|
||||
|
||||
if (Input::file('image')) {
|
||||
|
||||
$image = Input::file('image');
|
||||
$file_name = str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
$path = app('models_upload_path');
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
$model->image = $file_name;
|
||||
|
||||
}
|
||||
$model = $request->handleImages($model,600, public_path().'/uploads/models');
|
||||
|
||||
// Was it created?
|
||||
if ($model->save()) {
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
|
||||
}
|
||||
|
||||
// Redirect to the new model page
|
||||
return redirect()->route("models.index")->with('success', trans('admin/models/message.create.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($model->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores new Asset Model data created from the
|
||||
* modal form on the Asset Creation view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @param Request $request
|
||||
* @return String JSON
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
//COPYPASTA!!!! FIXME
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$model = new AssetModel;
|
||||
|
||||
$settings=Input::all();
|
||||
$settings['eol']= null;
|
||||
|
||||
$model->name=$request->input('name');
|
||||
$model->manufacturer_id = $request->input('manufacturer_id');
|
||||
$model->category_id = $request->input('category_id');
|
||||
$model->model_number = $request->input('model_number');
|
||||
$model->user_id = Auth::id();
|
||||
$model->notes = $request->input('notes');
|
||||
$model->eol= null;
|
||||
|
||||
if ($request->input('fieldset_id')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = e($request->input('fieldset_id'));
|
||||
}
|
||||
|
||||
if ($model->save()) {
|
||||
return JsonResponse::create($model);
|
||||
} else {
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($model->getErrors()->all('<li>:message</li>'), true)], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view containing the asset model edit form.
|
||||
*
|
||||
@@ -166,7 +114,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function edit($modelId = null)
|
||||
{
|
||||
$this->authorize('edit', AssetModel::class);
|
||||
$this->authorize('update', AssetModel::class);
|
||||
if ($item = AssetModel::find($modelId)) {
|
||||
$category_type = 'asset';
|
||||
$view = View::make('models/edit', compact('item','category_type'));
|
||||
@@ -190,7 +138,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $modelId = null)
|
||||
{
|
||||
$this->authorize('edit', AssetModel::class);
|
||||
$this->authorize('update', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model = AssetModel::find($modelId))) {
|
||||
// Redirect to the models management page
|
||||
@@ -206,43 +154,19 @@ class AssetModelsController extends Controller
|
||||
$model->notes = $request->input('notes');
|
||||
$model->requestable = $request->input('requestable', '0');
|
||||
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if ($request->input('custom_fieldset')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = $request->input('custom_fieldset');
|
||||
}
|
||||
|
||||
$old_image = $model->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$model->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $model->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('models_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('models_upload_path'), $file_name);
|
||||
}
|
||||
$model->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('models_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
|
||||
}
|
||||
}
|
||||
|
||||
$model = $request->handleImages($model,600, public_path().'/uploads/models');
|
||||
|
||||
if ($model->save()) {
|
||||
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
|
||||
@@ -276,7 +200,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$model->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,11 +259,8 @@ class AssetModelsController extends Controller
|
||||
if (isset($model->id)) {
|
||||
return view('models/view', compact('model'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/models/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('models.index')->with('error', $error);
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,9 +283,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
// Show the page
|
||||
$view = View::make('models/edit');
|
||||
$view->with('category_list', Helper::categoryList('asset'));
|
||||
$view->with('depreciation_list', Helper::depreciationList());
|
||||
$view->with('manufacturer_list', Helper::manufacturerList());
|
||||
$view->with('item', $model);
|
||||
$view->with('clone_model', $model_to_clone);
|
||||
return $view;
|
||||
@@ -398,14 +317,14 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
|
||||
|
||||
$models_raw_array = Input::get('ids');
|
||||
|
||||
// Make sure some IDs have been selected
|
||||
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
|
||||
|
||||
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets')->orderBy('assets_count', 'ASC')->get();
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->orderBy('assets_count', 'ASC')->get();
|
||||
|
||||
// If deleting....
|
||||
if ($request->input('bulk_actions')=='delete') {
|
||||
@@ -423,13 +342,8 @@ class AssetModelsController extends Controller
|
||||
$nochange = ['NC' => 'No Change'];
|
||||
$fieldset_list = $nochange + Helper::customFieldsetList();
|
||||
$depreciation_list = $nochange + Helper::depreciationList();
|
||||
$category_list = $nochange + Helper::categoryList('asset');
|
||||
$manufacturer_list = $nochange + Helper::manufacturerList();
|
||||
|
||||
|
||||
return view('models/bulk-edit', compact('models'))
|
||||
->with('manufacturer_list', $manufacturer_list)
|
||||
->with('category_list', $category_list)
|
||||
->with('fieldset_list', $fieldset_list)
|
||||
->with('depreciation_list', $depreciation_list);
|
||||
}
|
||||
@@ -457,10 +371,10 @@ class AssetModelsController extends Controller
|
||||
$update_array = array();
|
||||
|
||||
|
||||
if (($request->has('manufacturer_id') && ($request->input('manufacturer_id')!='NC'))) {
|
||||
if (($request->filled('manufacturer_id') && ($request->input('manufacturer_id')!='NC'))) {
|
||||
$update_array['manufacturer_id'] = $request->input('manufacturer_id');
|
||||
}
|
||||
if (($request->has('category_id') && ($request->input('category_id')!='NC'))) {
|
||||
if (($request->filled('category_id') && ($request->input('category_id')!='NC'))) {
|
||||
$update_array['category_id'] = $request->input('category_id');
|
||||
}
|
||||
if ($request->input('fieldset_id')!='NC') {
|
||||
@@ -498,7 +412,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
|
||||
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets')->get();
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->get();
|
||||
|
||||
$del_error_count = 0;
|
||||
$del_count = 0;
|
||||
@@ -531,4 +445,43 @@ class AssetModelsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a fieldset is set, 'add default values' is ticked and if
|
||||
* any default values were entered into the form.
|
||||
*
|
||||
* @param array $input
|
||||
* @return boolean
|
||||
*/
|
||||
private function shouldAddDefaultValues(array $input)
|
||||
{
|
||||
return !empty($input['add_default_values'])
|
||||
&& !empty($input['default_values'])
|
||||
&& !empty($input['custom_fieldset']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default values to a model (as long as they are truthy)
|
||||
*
|
||||
* @param AssetModel $model
|
||||
* @param array $defaultValues
|
||||
* @return void
|
||||
*/
|
||||
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $customFieldId => $defaultValue) {
|
||||
if ($defaultValue) {
|
||||
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all default values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function removeCustomFieldsDefaultValues(AssetModel $model)
|
||||
{
|
||||
$model->defaultValues()->detach();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
@@ -41,6 +42,8 @@ class ForgotPasswordController extends Controller
|
||||
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
@@ -49,22 +52,51 @@ class ForgotPasswordController extends Controller
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$this->validate($request, ['email' => 'required|email']);
|
||||
|
||||
// We will send the password reset link to this user. Once we have attempted
|
||||
// to send the link, we will examine the response then see the message we
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
/**
|
||||
* Let's set a max character count here to prevent potential
|
||||
* buffer overflow issues with attackers sending very large
|
||||
* payloads through.
|
||||
*/
|
||||
$this->validate($request, ['email' => 'required|email|max:250']);
|
||||
|
||||
/**
|
||||
* If we find a matching email with an activated user, we will
|
||||
* send the password reset link to the user.
|
||||
*
|
||||
* Once we have attempted to send the link, we will examine the response
|
||||
* then see the message we need to show to the user. Finally, we'll send out a proper response.
|
||||
*/
|
||||
$response = $this->broker()->sendResetLink(
|
||||
$request->only('email')
|
||||
array_merge(
|
||||
$request->only('email'),
|
||||
['activated' => '1']
|
||||
)
|
||||
);
|
||||
|
||||
if ($response === \Password::RESET_LINK_SENT) {
|
||||
return redirect()->route('login')->with('status', trans($response));
|
||||
}
|
||||
|
||||
// If an error was returned by the password broker, we will get this message
|
||||
// translated so we can notify a user of the problem. We'll redirect back
|
||||
// to where the users came from so they can attempt this process again.
|
||||
|
||||
/**
|
||||
* If an error was returned by the password broker, we will get this message
|
||||
* translated so we can notify a user of the problem. We'll redirect back
|
||||
* to where the users came from so they can attempt this process again.
|
||||
*
|
||||
* HOWEVER, we do not want to translate the message if the user isn't found
|
||||
* or isn't active, since that would allow an attacker to walk through
|
||||
* a dictionary attack and figure out registered user email addresses.
|
||||
*
|
||||
* Instead we tell the user we've sent an email even though we haven't.
|
||||
* It's bad UX, but better security. The compromises we sometimes have to make.
|
||||
*/
|
||||
|
||||
if ($response == 'passwords.user') {
|
||||
\Log::debug('User with email '.$request->input('email').' attempted a password reset request but was not found. No email was sent.');
|
||||
return redirect()->route('login')->with('success', trans('passwords.user_inactive'));
|
||||
}
|
||||
|
||||
return back()->withErrors(
|
||||
['email' => trans($response)]
|
||||
);
|
||||
|
||||
@@ -50,47 +50,72 @@ class LoginController extends Controller
|
||||
\Session::put('backUrl', \URL::previous());
|
||||
}
|
||||
|
||||
|
||||
function showLoginForm()
|
||||
function showLoginForm(Request $request)
|
||||
{
|
||||
$this->loginViaRemoteUser($request);
|
||||
if (Auth::check()) {
|
||||
return redirect()->intended('dashboard');
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->login_common_disabled == "1") {
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
|
||||
private function login_via_ldap(Request $request)
|
||||
private function loginViaRemoteUser(Request $request)
|
||||
{
|
||||
LOG::debug("Binding user to LDAP.");
|
||||
$remote_user = $request->server('REMOTE_USER');
|
||||
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
|
||||
Log::debug("Authenticatiing via REMOTE_USER.");
|
||||
|
||||
$pos = strpos($remote_user, '\\');
|
||||
if ($pos > 0) {
|
||||
$remote_user = substr($remote_user, $pos + 1);
|
||||
};
|
||||
|
||||
try {
|
||||
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
|
||||
Log::debug("Remote user auth lookup complete");
|
||||
if(!is_null($user)) Auth::login($user, true);
|
||||
} catch(Exception $e) {
|
||||
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loginViaLdap(Request $request)
|
||||
{
|
||||
Log::debug("Binding user to LDAP.");
|
||||
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
|
||||
if (!$ldap_user) {
|
||||
LOG::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
|
||||
Log::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
|
||||
throw new \Exception("Could not find user in LDAP directory");
|
||||
} else {
|
||||
LOG::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
|
||||
Log::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
|
||||
}
|
||||
|
||||
// Check if the user already exists in the database and was imported via LDAP
|
||||
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->first();
|
||||
LOG::debug("Local auth lookup complete");
|
||||
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->where('activated', '=', '1')->first();
|
||||
Log::debug("Local auth lookup complete");
|
||||
|
||||
// The user does not exist in the database. Try to get them from LDAP.
|
||||
// If user does not exist and authenticates successfully with LDAP we
|
||||
// will create it on the fly and sign in with default permissions
|
||||
if (!$user) {
|
||||
LOG::debug("Local user ".Input::get('username')." does not exist");
|
||||
LOG::debug("Creating local user ".Input::get('username'));
|
||||
Log::debug("Local user ".Input::get('username')." does not exist");
|
||||
Log::debug("Creating local user ".Input::get('username'));
|
||||
|
||||
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
|
||||
LOG::debug("Local user created.");
|
||||
Log::debug("Local user created.");
|
||||
} else {
|
||||
LOG::debug("Could not create local user.");
|
||||
Log::debug("Could not create local user.");
|
||||
throw new \Exception("Could not create local user");
|
||||
}
|
||||
// If the user exists and they were imported from LDAP already
|
||||
} else {
|
||||
LOG::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
|
||||
Log::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
|
||||
|
||||
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
|
||||
|
||||
@@ -114,6 +139,10 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
if (Setting::getSettings()->login_common_disabled == "1") {
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
$validator = $this->validator(Input::all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -132,29 +161,29 @@ class LoginController extends Controller
|
||||
|
||||
// Should we even check for LDAP users?
|
||||
if (Setting::getSettings()->ldap_enabled=='1') {
|
||||
LOG::debug("LDAP is enabled.");
|
||||
Log::debug("LDAP is enabled.");
|
||||
try {
|
||||
$user = $this->login_via_ldap($request);
|
||||
$user = $this->loginViaLdap($request);
|
||||
Auth::login($user, true);
|
||||
|
||||
// If the user was unable to login via LDAP, log the error and let them fall through to
|
||||
// local authentication.
|
||||
} catch (\Exception $e) {
|
||||
LOG::error("There was an error authenticating the LDAP user: ".$e->getMessage());
|
||||
Log::debug("There was an error authenticating the LDAP user: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// If the user wasn't authenticated via LDAP, skip to local auth
|
||||
if (!$user) {
|
||||
LOG::debug("Authenticating user against database.");
|
||||
Log::debug("Authenticating user against database.");
|
||||
// Try to log the user in
|
||||
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
|
||||
if (!Auth::attempt(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
|
||||
|
||||
if (!$lockedOut) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
}
|
||||
|
||||
LOG::debug("Local authentication failed.");
|
||||
Log::debug("Local authentication failed.");
|
||||
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
|
||||
} else {
|
||||
|
||||
@@ -164,7 +193,6 @@ class LoginController extends Controller
|
||||
|
||||
if ($user = Auth::user()) {
|
||||
$user->last_login = \Carbon::now();
|
||||
\Log::debug('Last login:'.$user->last_login);
|
||||
$user->save();
|
||||
}
|
||||
// Redirect to the users page
|
||||
@@ -180,26 +208,33 @@ class LoginController extends Controller
|
||||
public function getTwoFactorEnroll()
|
||||
{
|
||||
|
||||
// Make sure the user is logged in
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$user = Auth::user();
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
|
||||
if ($user->two_factor_secret=='') {
|
||||
$user->two_factor_secret = $google2fa->generateSecretKey(32);
|
||||
$user->save();
|
||||
// We wouldn't normally see this page if 2FA isn't enforced via the
|
||||
// \App\Http\Middleware\CheckForTwoFactor middleware AND if a device isn't enrolled,
|
||||
// but let's check check anyway in case there's a browser history or back button thing.
|
||||
// While you can access this page directly, enrolling a device when 2FA isn't enforced
|
||||
// won't cause any harm.
|
||||
|
||||
if (($user->two_factor_secret!='') && ($user->two_factor_enrolled==1)) {
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.already_enrolled'));
|
||||
}
|
||||
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $google2fa->generateSecretKey();
|
||||
$user->two_factor_secret = $secret;
|
||||
$user->save();
|
||||
|
||||
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
|
||||
urlencode(Setting::getSettings()->site_name),
|
||||
urlencode($user->username),
|
||||
$user->two_factor_secret
|
||||
);
|
||||
|
||||
return view('auth.two_factor_enroll')->with('google2fa_url', $google2fa_url);
|
||||
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
||||
$barcode_obj = $barcode->getBarcodeObj('QRCODE', 'otpauth://totp/'.urlencode($settings->site_name).':'.urlencode($user->username).'?secret='.urlencode($secret).'&issuer=Snipe-IT&period=30', 300, 300, 'black', array(-2, -2, -2, -2));
|
||||
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
|
||||
|
||||
}
|
||||
|
||||
@@ -211,6 +246,20 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function getTwoFactorAuth()
|
||||
{
|
||||
// Check that the user is logged in
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
// Check whether there is a device enrolled.
|
||||
// This *should* be handled via the \App\Http\Middleware\CheckForTwoFactor middleware
|
||||
// but we're just making sure (in case someone edited the database directly, etc)
|
||||
if (($user->two_factor_secret=='') || ($user->two_factor_enrolled!=1)) {
|
||||
return redirect()->route('two-factor-enroll');
|
||||
}
|
||||
|
||||
return view('auth.two_factor');
|
||||
}
|
||||
|
||||
@@ -223,22 +272,25 @@ class LoginController extends Controller
|
||||
{
|
||||
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
if (!$request->filled('two_factor_secret')) {
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.code_required'));
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$secret = $request->get('two_factor_secret');
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $request->input('two_factor_secret');
|
||||
|
||||
if ($valid) {
|
||||
if ($google2fa->verifyKey($user->two_factor_secret, $secret)) {
|
||||
$user->two_factor_enrolled = 1;
|
||||
$user->save();
|
||||
$request->session()->put('2fa_authed', 'true');
|
||||
return redirect()->route('home')->with('success', 'You are logged in!');
|
||||
}
|
||||
|
||||
return redirect()->route('two-factor')->with('error', 'Invalid two-factor code');
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.invalid_code'));
|
||||
|
||||
|
||||
}
|
||||
@@ -251,9 +303,17 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->session()->forget('2fa_authed');
|
||||
|
||||
$request->session()->regenerate(true);
|
||||
Auth::logout();
|
||||
return redirect()->route('login')->with('success', 'You have successfully logged out!');
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$customLogoutUrl = $settings->login_remote_user_custom_logout_url ;
|
||||
if ($settings->login_remote_user_enabled == '1' && $customLogoutUrl != '') {
|
||||
return redirect()->away($customLogoutUrl);
|
||||
}
|
||||
|
||||
return redirect()->route('login')->with('success', trans('auth/message.logout.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -278,11 +338,11 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the user after determining they are locked out.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
* Redirect the user after determining they are locked out.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendLockoutResponse(Request $request)
|
||||
{
|
||||
$seconds = $this->limiter()->availableIn(
|
||||
@@ -293,18 +353,18 @@ class LoginController extends Controller
|
||||
|
||||
$message = \Lang::get('auth/message.throttle', ['minutes' => $minutes]);
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->back()
|
||||
->withInput($request->only($this->username(), 'remember'))
|
||||
->withErrors([$this->username() => $message]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override the lockout time and duration
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
* Override the lockout time and duration
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasTooManyLoginAttempts(Request $request)
|
||||
{
|
||||
$lockoutTime = config('auth.throttle.lockout_duration');
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
@@ -36,4 +38,8 @@ class ResetPasswordController extends Controller
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
249
app/Http/Controllers/BulkAssetsController.php
Normal file
249
app/Http/Controllers/BulkAssetsController.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BulkAssetsController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function edit(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if (!$request->filled('ids')) {
|
||||
return redirect()->back()->with('error', 'No assets selected');
|
||||
}
|
||||
|
||||
$asset_ids = array_keys($request->input('ids'));
|
||||
|
||||
if ($request->filled('bulk_actions')) {
|
||||
switch($request->input('bulk_actions')) {
|
||||
case 'labels':
|
||||
return view('hardware/labels')
|
||||
->with('assets', Asset::find($asset_ids))
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('count', 0);
|
||||
case 'delete':
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
case 'edit':
|
||||
return view('hardware/bulk')
|
||||
->with('assets', request('ids'))
|
||||
->with('statuslabel_list', Helper::statusLabelList());
|
||||
}
|
||||
}
|
||||
return redirect()->back()->with('error', 'No action selected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk edits
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
\Log::debug($request->input('ids'));
|
||||
|
||||
if(!$request->filled('ids') || count($request->input('ids')) <= 0) {
|
||||
return redirect()->route("hardware.index")->with('warning', trans('No assets selected, so nothing was updated.'));
|
||||
}
|
||||
|
||||
$assets = array_keys($request->input('ids'));
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
|| ($request->filled('supplier_id'))
|
||||
|| ($request->filled('order_number'))
|
||||
|| ($request->filled('warranty_months'))
|
||||
|| ($request->filled('rtd_location_id'))
|
||||
|| ($request->filled('requestable'))
|
||||
|| ($request->filled('company_id'))
|
||||
|| ($request->filled('status_id'))
|
||||
|| ($request->filled('model_id'))
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
$this->update_array = [];
|
||||
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('model_id')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
->conditionallyAddItem('status_id')
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months');
|
||||
|
||||
if ($request->filled('purchase_cost')) {
|
||||
$this->update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$this->update_array['company_id'] = $request->input('company_id');
|
||||
if ($request->input('company_id')=="clear") {
|
||||
$this->update_array['company_id'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('rtd_location_id')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
}
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $assetId)
|
||||
->update($this->update_array);
|
||||
} // endforeach
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.update.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->route("hardware.index")->with('warning', trans('admin/hardware/message.update.nothing_updated'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Array to store update data per item
|
||||
* @var Array
|
||||
*/
|
||||
private $update_array;
|
||||
/**
|
||||
* Adds parameter to update array for an item if it exists in request
|
||||
* @param String $field field name
|
||||
* @return this Model for Chaining
|
||||
*/
|
||||
protected function conditionallyAddItem($field)
|
||||
{
|
||||
if(request()->filled($field)) {
|
||||
$this->update_array[$field] = request()->input($field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk deleted.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
if ($request->filled('ids')) {
|
||||
$assets = Asset::find($request->get('ids'));
|
||||
foreach ($assets as $asset) {
|
||||
$update_array['deleted_at'] = date('Y-m-d H:i:s');
|
||||
$update_array['assigned_to'] = null;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update($update_array);
|
||||
} // endforeach
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.delete.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->to("hardware")->with('info', trans('admin/hardware/message.delete.nothing_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Bulk Checkout Page
|
||||
* @return View View to checkout multiple assets
|
||||
*/
|
||||
public function showCheckout()
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
// Filter out assets that are not deployable.
|
||||
|
||||
return view('hardware/bulk-checkout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Multiple Checkout Request
|
||||
* @return View
|
||||
*/
|
||||
public function storeCheckout(Request $request)
|
||||
{
|
||||
try {
|
||||
$admin = Auth::user();
|
||||
|
||||
$target = $this->determineCheckoutTarget();
|
||||
|
||||
if (!is_array($request->get('selected_assets'))) {
|
||||
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
|
||||
}
|
||||
|
||||
$asset_ids = array_filter($request->get('selected_assets'));
|
||||
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
if ($target->id == $asset_id && request('checkout_to_type') =='asset') {
|
||||
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
|
||||
}
|
||||
}
|
||||
$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'));
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$expected_checkin = e($request->get('expected_checkin'));
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
|
||||
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$this->authorize('checkout', $asset);
|
||||
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), null);
|
||||
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = $target->location_id;
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
array_merge_recursive($errors, $asset->getErrors()->toArray());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!$errors) {
|
||||
// Redirect to the new asset page
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->to("hardware/bulk-checkout")->with('error', $e->getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,17 +83,7 @@ class CategoriesController extends Controller
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->user_id = Auth::id();
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/categories/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$category->image = $file_name;
|
||||
}
|
||||
|
||||
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
|
||||
|
||||
if ($category->save()) {
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.create.success'));
|
||||
@@ -152,37 +142,12 @@ class CategoriesController extends Controller
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
|
||||
$old_image = $category->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$category->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $category->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('categories_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('categories_upload_path'), $file_name);
|
||||
}
|
||||
$category->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('categories_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
|
||||
|
||||
if ($category->save()) {
|
||||
// Redirect to the new category page
|
||||
@@ -254,10 +219,7 @@ class CategoriesController extends Controller
|
||||
->with('category_type_route',$category_type_route);
|
||||
}
|
||||
|
||||
// Prepare the error message
|
||||
$error = trans('admin/categories/message.does_not_exist', compact('id'));
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('categories.index')->with('error', $error);
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
app/Http/Controllers/CheckInOutRequest.php
Normal file
56
app/Http/Controllers/CheckInOutRequest.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
|
||||
trait CheckInOutRequest
|
||||
{
|
||||
/**
|
||||
* Find target for checkout
|
||||
* @return SnipeModel Target asset is being checked out to.
|
||||
*/
|
||||
protected function determineCheckoutTarget()
|
||||
{
|
||||
// This item is checked out to a location
|
||||
switch(request('checkout_to_type'))
|
||||
{
|
||||
case 'location':
|
||||
return Location::findOrFail(request('assigned_location'));
|
||||
case 'asset':
|
||||
return Asset::findOrFail(request('assigned_asset'));
|
||||
case 'user':
|
||||
return User::findOrFail(request('assigned_user'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the location of the asset passed in.
|
||||
* @param Asset $asset Asset being updated
|
||||
* @param SnipeModel $target Target with location
|
||||
* @return Asset Asset being updated
|
||||
*/
|
||||
protected function updateAssetLocation($asset, $target)
|
||||
{
|
||||
switch(request('checkout_to_type'))
|
||||
{
|
||||
case 'location':
|
||||
$asset->location_id = $target->id;
|
||||
break;
|
||||
case 'asset':
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
// Override with the asset's location_id if it has one
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = $target->location_id;
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
$asset->location_id = $target->location_id;
|
||||
break;
|
||||
}
|
||||
return $asset;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,9 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('companies/index')->with('companies', Company::all());
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
return view('companies/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,6 +43,8 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
return view('companies/edit')->with('item', new Company);
|
||||
}
|
||||
|
||||
@@ -54,19 +58,12 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
$company = new Company;
|
||||
$company->name = $request->input('name');
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/companies/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$company->image = $file_name;
|
||||
}
|
||||
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->route('companies.index')
|
||||
@@ -90,6 +87,9 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('companies/edit')->with('item', $item);
|
||||
}
|
||||
|
||||
@@ -108,38 +108,16 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$company->name = $request->input('name');
|
||||
$this->authorize('update', $company);
|
||||
|
||||
$old_image = $company->image;
|
||||
$company->name = $request->input('name');
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$company->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $company->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('companies_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('companies_upload_path'), $file_name);
|
||||
}
|
||||
$company->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('companies_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
|
||||
|
||||
|
||||
if ($company->save()) {
|
||||
@@ -164,6 +142,9 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.not_found'));
|
||||
} else {
|
||||
|
||||
$this->authorize('delete', $company);
|
||||
|
||||
try {
|
||||
$company->delete();
|
||||
return redirect()->route('companies.index')
|
||||
|
||||
@@ -91,16 +91,7 @@ class ComponentsController extends Controller
|
||||
$component->user_id = Auth::id();
|
||||
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/components/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$component->image = $file_name;
|
||||
}
|
||||
$component = $request->handleImages($component,600, public_path().'/uploads/components');
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
|
||||
@@ -164,18 +155,7 @@ class ComponentsController extends Controller
|
||||
$component->purchase_cost = request('purchase_cost');
|
||||
$component->qty = Input::get('qty');
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/components/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$component->image = $file_name;
|
||||
} elseif ($request->input('image_delete')=='1') {
|
||||
$component->image = null;
|
||||
}
|
||||
$component = $request->handleImages($component,600, public_path().'/uploads/components');
|
||||
|
||||
if ($component->save()) {
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
|
||||
@@ -194,7 +174,7 @@ class ComponentsController extends Controller
|
||||
public function destroy($componentId)
|
||||
{
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('delete', $component);
|
||||
@@ -202,19 +182,6 @@ class ComponentsController extends Controller
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
|
||||
}
|
||||
|
||||
public function postBulk($componentId = null)
|
||||
{
|
||||
//$this->authorize('checkout', $component)
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
public function postBulkSave($componentId = null)
|
||||
{
|
||||
//$this->authorize('edit', Component::class);
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a view to display component information.
|
||||
*
|
||||
@@ -232,10 +199,8 @@ class ComponentsController extends Controller
|
||||
$this->authorize('view', $component);
|
||||
return view('components/view', compact('component'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/components/message.does_not_exist', compact('id'));
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('components.index')->with('error', $error);
|
||||
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,13 +7,11 @@ use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutNotification;
|
||||
use Auth;
|
||||
use Config;
|
||||
use DB;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Mail;
|
||||
use Redirect;
|
||||
use Slack;
|
||||
use Str;
|
||||
@@ -89,16 +87,8 @@ class ConsumablesController extends Controller
|
||||
$consumable->user_id = Auth::id();
|
||||
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/consumables/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$consumable->image = $file_name;
|
||||
}
|
||||
$consumable = $request->handleImages($consumable,600, public_path().'/uploads/components');
|
||||
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
|
||||
@@ -164,7 +154,7 @@ class ConsumablesController extends Controller
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/consumables/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
@@ -214,7 +204,7 @@ class ConsumablesController extends Controller
|
||||
if (isset($consumable->id)) {
|
||||
return view('consumables/view', compact('consumable'));
|
||||
}
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,14 +269,6 @@ class ConsumablesController extends Controller
|
||||
$data['note'] = $logaction->note;
|
||||
$data['require_acceptance'] = $consumable->requireAcceptance();
|
||||
|
||||
if ((($consumable->requireAcceptance()=='1') || ($consumable->getEula())) && $user->email!='') {
|
||||
|
||||
Mail::send('emails.accept-asset', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Confirm_consumable_delivery'));
|
||||
});
|
||||
}
|
||||
|
||||
// Redirect to the new consumable page
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
|
||||
@@ -37,6 +37,7 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', CustomField::class);
|
||||
|
||||
$fieldsets = CustomFieldset::with("fields", "models")->get();
|
||||
$fields = CustomField::with("fieldset")->get();
|
||||
@@ -57,6 +58,7 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', new CustomField());
|
||||
}
|
||||
@@ -72,17 +74,20 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function store(CustomFieldRequest $request)
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$field = new CustomField([
|
||||
"name" => $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),
|
||||
"show_in_email" => $request->get("show_in_email", 0),
|
||||
"user_id" => Auth::user()->id
|
||||
]);
|
||||
|
||||
|
||||
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
|
||||
if ($request->has("custom_format")) {
|
||||
$field->format = e($request->get("custom_format"));
|
||||
} else {
|
||||
$field->format = e($request->get("format"));
|
||||
@@ -91,7 +96,6 @@ class CustomFieldsController extends Controller
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
|
||||
} else {
|
||||
// dd($field);
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
|
||||
}
|
||||
|
||||
@@ -109,6 +113,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
if ($field->fieldset()->detach($fieldset_id)) {
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
}
|
||||
@@ -127,6 +133,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->fieldset->count()>0) {
|
||||
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
|
||||
} else {
|
||||
@@ -148,6 +156,9 @@ class CustomFieldsController extends Controller
|
||||
public function edit($id)
|
||||
{
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', $field);
|
||||
}
|
||||
|
||||
@@ -166,12 +177,14 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
$field->name = e($request->get("name"));
|
||||
$field->element = e($request->get("element"));
|
||||
$field->field_values = e($request->get("field_values"));
|
||||
$field->field_encrypted = e($request->get("field_encrypted", 0));
|
||||
$field->user_id = Auth::user()->id;
|
||||
$field->help_text = $request->get("help_text");
|
||||
$field->show_in_email = $request->get("show_in_email", 0);
|
||||
|
||||
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
|
||||
$field->format = e($request->get("custom_format"));
|
||||
@@ -179,7 +192,6 @@ class CustomFieldsController extends Controller
|
||||
$field->format = e($request->get("format"));
|
||||
}
|
||||
|
||||
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
|
||||
}
|
||||
|
||||
@@ -38,11 +38,13 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
|
||||
|
||||
$this->authorize('view', $cfset);
|
||||
|
||||
if ($cfset) {
|
||||
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
|
||||
|
||||
$maxid = 0;
|
||||
foreach ($cfset->fields() as $field) {
|
||||
foreach ($cfset->fields as $field) {
|
||||
if ($field->pivot->order > $maxid) {
|
||||
$maxid=$field->pivot->order;
|
||||
}
|
||||
@@ -68,6 +70,8 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
return view("custom_fields.fieldsets.edit");
|
||||
}
|
||||
|
||||
@@ -81,6 +85,8 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
$cfset = new CustomFieldset(
|
||||
[
|
||||
"name" => e($request->get("name")),
|
||||
@@ -141,6 +147,8 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('delete', $fieldset);
|
||||
|
||||
if ($fieldset) {
|
||||
$models = AssetModel::where("fieldset_id", "=", $id);
|
||||
if ($models->count() == 0) {
|
||||
@@ -164,19 +172,66 @@ class CustomFieldsetsController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function associate($id)
|
||||
public function associate(Request $request, $id)
|
||||
{
|
||||
|
||||
$set = CustomFieldset::find($id);
|
||||
|
||||
foreach ($set->fields as $field) {
|
||||
if ($field->id == Input::get('field_id')) {
|
||||
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
|
||||
$this->authorize('update', $set);
|
||||
|
||||
if ($request->filled('field_id')) {
|
||||
foreach ($set->fields as $field) {
|
||||
if ($field->id == $request->input('field_id')) {
|
||||
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
|
||||
}
|
||||
}
|
||||
|
||||
$results = $set->fields()->attach(Input::get('field_id'), ["required" => ($request->input('required') == "on"),"order" => $request->input('order', 1)]);
|
||||
|
||||
return redirect()->route("fieldsets.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
|
||||
}
|
||||
return redirect()->route("fieldsets.show", [$id])->with("error", 'No field selected.');
|
||||
|
||||
$results=$set->fields()->attach(Input::get('field_id'), ["required" => (Input::get('required') == "on"),"order" => Input::get('order')]);
|
||||
|
||||
return redirect()->route("fieldsets.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field in a fieldset to required
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
*/
|
||||
public function makeFieldRequired($fieldset_id, $field_id)
|
||||
{
|
||||
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
$fields[$field->id] = ['required' => 1];
|
||||
$fieldset->fields()->syncWithoutDetaching($fields);
|
||||
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
|
||||
->with("success", trans('Field successfully set to required'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field in a fieldset to optional
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
*/
|
||||
public function makeFieldOptional($fieldset_id, $field_id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
$fields[$field->id] = ['required' => 0];
|
||||
$fieldset->fields()->syncWithoutDetaching($fields);
|
||||
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
|
||||
->with("success", trans('Field successfully set to optional'));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class DepartmentsController extends Controller
|
||||
{
|
||||
$this->authorize('index', Department::class);
|
||||
$company = null;
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$company = Company::find($request->input('company_id'));
|
||||
}
|
||||
return view('departments/index')->with('company', $company);
|
||||
@@ -51,18 +51,9 @@ class DepartmentsController extends Controller
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department->user_id = Auth::user()->id;
|
||||
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/departments/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$department->image = $file_name;
|
||||
}
|
||||
$department = $request->handleImages($department,600, public_path().'/uploads/departments');
|
||||
|
||||
if ($department->save()) {
|
||||
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.create.success'));
|
||||
@@ -83,10 +74,12 @@ class DepartmentsController extends Controller
|
||||
{
|
||||
$department = Department::find($id);
|
||||
|
||||
$this->authorize('view', $department);
|
||||
|
||||
if (isset($department->id)) {
|
||||
return view('departments/view', compact('department'));
|
||||
}
|
||||
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +93,8 @@ class DepartmentsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
|
||||
return view('departments/edit')->with('item', new Department);
|
||||
}
|
||||
|
||||
@@ -118,6 +113,8 @@ class DepartmentsController extends Controller
|
||||
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('delete', $department);
|
||||
|
||||
if ($department->users->count() > 0) {
|
||||
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.assoc_users'));
|
||||
}
|
||||
@@ -141,49 +138,24 @@ class DepartmentsController extends Controller
|
||||
if (is_null($item = Department::find($id))) {
|
||||
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('departments/edit', compact('item'));
|
||||
}
|
||||
|
||||
public function update(ImageUploadRequest $request, $id) {
|
||||
|
||||
$this->authorize('create', Department::class);
|
||||
if (is_null($department = Department::find($id))) {
|
||||
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $department);
|
||||
|
||||
$department->fill($request->all());
|
||||
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
$old_image = $department->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$department->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $department->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('departments_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('departments_upload_path'), $file_name);
|
||||
}
|
||||
$department->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('departments_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
$department = $request->handleImages($department,600, public_path().'/uploads/departments');
|
||||
|
||||
if ($department->save()) {
|
||||
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.update.success'));
|
||||
|
||||
@@ -31,8 +31,10 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return view('depreciations/index', compact('depreciations'));
|
||||
return view('depreciations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +48,8 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return view('depreciations/edit')->with('item', new Depreciation);
|
||||
}
|
||||
@@ -62,6 +66,8 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// create a new instance
|
||||
$depreciation = new Depreciation();
|
||||
// Depreciation data
|
||||
@@ -94,6 +100,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('depreciations/edit', compact('item'));
|
||||
}
|
||||
|
||||
@@ -116,6 +124,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $depreciation);
|
||||
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
@@ -145,6 +155,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('delete', $depreciation);
|
||||
|
||||
if ($depreciation->has_models() > 0) {
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.assoc_users'));
|
||||
@@ -171,6 +183,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('view', $depreciation);
|
||||
|
||||
return view('depreciations/view', compact('depreciation'));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class GroupsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return view('groups/index', compact('groups'));
|
||||
return view('groups/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ class GroupsController extends Controller
|
||||
if ($group->save()) {
|
||||
return redirect()->route("groups.index")->with('success', trans('admin/groups/message.success.create'));
|
||||
}
|
||||
return redirect(route('groups.create'))->withInput()->withErrors($group->getErrors());
|
||||
return redirect()->back()->withInput()->withErrors($group->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +111,7 @@ class GroupsController extends Controller
|
||||
{
|
||||
$permissions = config('permissions');
|
||||
if (!$group = Group::find($id)) {
|
||||
return redirect()->route('groups')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
$group->name = e(Input::get('name'));
|
||||
$group->permissions = json_encode(Input::get('permission'));
|
||||
@@ -138,7 +138,7 @@ class GroupsController extends Controller
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
if (!$group = Group::find($id)) {
|
||||
return redirect()->route('groups')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
$group->delete();
|
||||
// Redirect to the group management page
|
||||
|
||||
@@ -5,12 +5,14 @@ namespace App\Http\Controllers;
|
||||
use App\Http\Transformers\ImportsTransformer;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Asset;
|
||||
|
||||
|
||||
class ImportsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
$imports = (new ImportsTransformer)->transformImports($imports);
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use Assets;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Input;
|
||||
@@ -107,6 +108,7 @@ class LicensesController extends Controller
|
||||
$license->seats = $request->input('seats');
|
||||
$license->serial = $request->input('serial');
|
||||
$license->supplier_id = $request->input('supplier_id');
|
||||
$license->category_id = $request->input('category_id');
|
||||
$license->termination_date = $request->input('termination_date');
|
||||
$license->user_id = Auth::id();
|
||||
|
||||
@@ -177,11 +179,14 @@ class LicensesController extends Controller
|
||||
$license->purchase_date = $request->input('purchase_date');
|
||||
$license->purchase_order = $request->input('purchase_order');
|
||||
$license->reassignable = $request->input('reassignable', 0);
|
||||
$license->serial = $request->input('serial');
|
||||
if (Gate::allows('viewKeys', $license)) {
|
||||
$license->serial = $request->input('serial');
|
||||
}
|
||||
$license->termination_date = $request->input('termination_date');
|
||||
$license->seats = e($request->input('seats'));
|
||||
$license->manufacturer_id = $request->input('manufacturer_id');
|
||||
$license->supplier_id = $request->input('supplier_id');
|
||||
$license->category_id = $request->input('category_id');
|
||||
|
||||
if ($license->save()) {
|
||||
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
|
||||
@@ -241,17 +246,23 @@ class LicensesController extends Controller
|
||||
*/
|
||||
public function getCheckout($licenceId)
|
||||
{
|
||||
|
||||
// Check that the license is valid
|
||||
if ($license = License::where('id',$licenceId)->first()) {
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
|
||||
// If the license is valid, check that there is an available seat
|
||||
if ($license->getAvailSeatsCountAttribute() < 1) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
return view('licenses/checkout', compact('license'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
return view('licenses/checkout', compact('license'));
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -262,31 +273,40 @@ class LicensesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param Request $request
|
||||
* @param int $licenseId
|
||||
* @param int $seatId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postCheckout(Request $request, $licenseId)
|
||||
public function postCheckout(Request $request, $licenseId, $seatId = null)
|
||||
{
|
||||
|
||||
// Check that the license is valid
|
||||
if ($license = License::where('id',$licenseId)->first()) {
|
||||
|
||||
if ($license = License::where('id', $licenseId)->first()) {
|
||||
|
||||
// If the license is valid, check that there is an available seat
|
||||
if ($license->getAvailSeatsCountAttribute() < 1) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
|
||||
// Get the next available seat for this license
|
||||
$next = $license->freeSeat();
|
||||
|
||||
if (!$next) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
if (!$seatId) {
|
||||
// Get the next available seat for this license
|
||||
$next = $license->freeSeat();
|
||||
if (!$next) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
} else {
|
||||
$licenseSeat = LicenseSeat::where('id', '=', $seatId)->first();
|
||||
if (!$licenseSeat) {
|
||||
return redirect()->route('licenses.index')->with('error', 'License seat is not available for checkout');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
|
||||
@@ -401,31 +421,19 @@ class LicensesController extends Controller
|
||||
return redirect()->back()->withInput();
|
||||
}
|
||||
|
||||
// Declare the rules for the form validation
|
||||
$rules = array(
|
||||
'note' => 'string',
|
||||
'notes' => 'string',
|
||||
);
|
||||
|
||||
// Create a new validator instance from our validation rules
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
|
||||
// If validation fails, we'll exit the operation now.
|
||||
if ($validator->fails()) {
|
||||
// Ooops.. something went wrong
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
if (!$return_to) {
|
||||
$return_to = Asset::find($licenseSeat->asset_id);
|
||||
}
|
||||
|
||||
// Update the asset data
|
||||
$licenseSeat->assigned_to = null;
|
||||
$licenseSeat->asset_id = null;
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
$licenseSeat->logCheckin($return_to, e(request('note')));
|
||||
$licenseSeat->logCheckin($license, e(request('note')));
|
||||
if ($backTo=='user') {
|
||||
return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
}
|
||||
@@ -453,7 +461,7 @@ class LicensesController extends Controller
|
||||
$this->authorize('view', $license);
|
||||
return view('licenses/view', compact('license'));
|
||||
}
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -492,7 +500,7 @@ class LicensesController extends Controller
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postUpload(Request $request, $licenseId = null)
|
||||
public function postUpload(AssetFileRequest $request, $licenseId = null)
|
||||
{
|
||||
$license = License::find($licenseId);
|
||||
// the license is valid
|
||||
@@ -501,21 +509,11 @@ class LicensesController extends Controller
|
||||
if (isset($license->id)) {
|
||||
$this->authorize('update', $license);
|
||||
|
||||
if (Input::hasFile('licensefile')) {
|
||||
if (Input::hasFile('file')) {
|
||||
|
||||
foreach (Input::file('licensefile') as $file) {
|
||||
|
||||
$rules = array(
|
||||
'licensefile' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar,rtf,xml,lic|max:2000'
|
||||
);
|
||||
$validator = Validator::make(array('licensefile'=> $file), $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->with('error', trans('admin/licenses/message.upload.invalidfiles'));
|
||||
}
|
||||
foreach (Input::file('file') as $file) {
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'license-'.$license->id.'-'.str_random(8);
|
||||
$filename .= '-'.str_slug($file->getClientOriginalName()).'.'.$extension;
|
||||
$filename = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$upload_success = $file->move($destinationPath, $filename);
|
||||
|
||||
//Log the upload to the log
|
||||
@@ -532,9 +530,8 @@ class LicensesController extends Controller
|
||||
}
|
||||
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
|
||||
return redirect()->route('licenses.index')->with('error', $error);
|
||||
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -553,21 +550,24 @@ class LicensesController extends Controller
|
||||
$destinationPath = config('app.private_uploads').'/licenses';
|
||||
|
||||
// the license is valid
|
||||
if (isset($license->id)) {
|
||||
if ($license) {
|
||||
$this->authorize('edit', $license);
|
||||
$log = Actionlog::find($fileId);
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
if ($log) {
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/licenses/message.deletefile.success'));
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/licenses/message.deletefile.success'));
|
||||
|
||||
return redirect()->back()->with('error', 'Could not locate that file.');
|
||||
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/licenses/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the licence management page
|
||||
return redirect()->route('licenses.index')->with('error', $error);
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -581,7 +581,7 @@ class LicensesController extends Controller
|
||||
* @param int $fileId
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function displayFile($licenseId = null, $fileId = null)
|
||||
public function displayFile($licenseId = null, $fileId = null, $download = true)
|
||||
{
|
||||
|
||||
$license = License::find($licenseId);
|
||||
@@ -590,10 +590,35 @@ class LicensesController extends Controller
|
||||
if (isset($license->id)) {
|
||||
$this->authorize('view', $license);
|
||||
$log = Actionlog::find($fileId);
|
||||
$file = $log->get_src('licenses');
|
||||
return Response::download($file);
|
||||
|
||||
if ($log) {
|
||||
|
||||
$file = $log->get_src('licenses');
|
||||
if ($file =='') {
|
||||
return response('File not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
$mimetype = \File::mimeType($file);
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return response('File '.$file.' not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
if ($download != 'true') {
|
||||
if ($contents = file_get_contents($file)) {
|
||||
return Response::make($contents)->header('Content-Type', $mimetype);
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: "], 500);
|
||||
}
|
||||
return Response::download($file);
|
||||
}
|
||||
|
||||
}
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', compact('id')));
|
||||
|
||||
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,8 @@ class LocationsController extends Controller
|
||||
{
|
||||
// Grab all the locations
|
||||
$this->authorize('view', Location::class);
|
||||
$locations = Location::orderBy('created_at', 'DESC')->with('parent', 'assets', 'assignedassets')->get();
|
||||
|
||||
// Show the page
|
||||
return view('locations/index', compact('locations'));
|
||||
return view('locations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +57,7 @@ class LocationsController extends Controller
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$locations = Location::orderBy('name', 'ASC')->get();
|
||||
|
||||
$location_options_array = Location::getLocationHierarchy($locations);
|
||||
$location_options = Location::flattenLocationsArray($location_options_array);
|
||||
$location_options = array('' => 'Top Level') + $location_options;
|
||||
|
||||
return view('locations/edit')
|
||||
->with('location_options', $location_options)
|
||||
->with('item', new Location);
|
||||
}
|
||||
|
||||
@@ -97,16 +88,7 @@ class LocationsController extends Controller
|
||||
$location->manager_id = $request->input('manager_id');
|
||||
$location->user_id = Auth::id();
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/locations/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(600, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$location->image = $file_name;
|
||||
}
|
||||
$location = $request->handleImages($location,600, public_path().'/uploads/locations');
|
||||
|
||||
if ($location->save()) {
|
||||
return redirect()->route("locations.index")->with('success', trans('admin/locations/message.create.success'));
|
||||
@@ -114,42 +96,6 @@ class LocationsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($location->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores a new location created via the Create Asset form modal.
|
||||
*
|
||||
* @todo Check if a Form Request would work better here.
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetsController::getCreate() method that makes the form
|
||||
* @since [v1.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$new['currency']=Setting::first()->default_currency;
|
||||
|
||||
// create a new location instance
|
||||
$location = new Location();
|
||||
|
||||
// Save the location data
|
||||
$location->name = $request->input('name');
|
||||
$location->currency = Setting::first()->default_currency; //e(Input::get('currency'));
|
||||
$location->address = ''; //e(Input::get('address'));
|
||||
// $location->address2 = e(Input::get('address2'));
|
||||
$location->city = $request->input('city');
|
||||
$location->state = '';//e(Input::get('state'));
|
||||
$location->country = $request->input('country');
|
||||
// $location->zip = e(Input::get('zip'));
|
||||
$location->user_id = Auth::id();
|
||||
|
||||
// Was the location created?
|
||||
if ($location->save()) {
|
||||
return JsonResponse::create($location);
|
||||
}
|
||||
// failure
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($location->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a form view to edit location information.
|
||||
@@ -162,21 +108,14 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function edit($locationId = null)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($item = Location::find($locationId))) {
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Show the page
|
||||
$locations = Location::orderBy('name', 'ASC')->get();
|
||||
$location_options_array = Location::getLocationHierarchy($locations);
|
||||
$location_options = Location::flattenLocationsArray($location_options_array);
|
||||
$location_options = array('' => 'Top Level') + $location_options;
|
||||
|
||||
return view('locations/edit', compact('item'))
|
||||
->with('location_options', $location_options)
|
||||
->with('manager_list', Helper::managerList());
|
||||
return view('locations/edit', compact('item'));
|
||||
}
|
||||
|
||||
|
||||
@@ -191,12 +130,17 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $locationId = null)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
if ($request->input('parent_id') == $locationId) {
|
||||
return redirect()->back()->withInput()->with('error', 'A location cannot be its own parent. Please select a different parent location.');
|
||||
}
|
||||
|
||||
|
||||
// Update the location data
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
@@ -209,37 +153,7 @@ class LocationsController extends Controller
|
||||
$location->zip = $request->input('zip');
|
||||
$location->ldap_ou = $request->input('ldap_ou');
|
||||
$location->manager_id = $request->input('manager_id');
|
||||
|
||||
$old_image = $location->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$location->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $location->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(600, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('locations_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('locations_upload_path'), $file_name);
|
||||
}
|
||||
$location->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('locations_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
$location = $request->handleImages($location,600, public_path().'/uploads/locations');
|
||||
|
||||
|
||||
if ($location->save()) {
|
||||
@@ -266,7 +180,7 @@ class LocationsController extends Controller
|
||||
if ($location->users->count() > 0) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_users'));
|
||||
|
||||
} elseif ($location->childLocations->count() > 0) {
|
||||
} elseif ($location->children->count() > 0) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_child_loc'));
|
||||
|
||||
} elseif ($location->assets->count() > 0) {
|
||||
@@ -299,7 +213,7 @@ class LocationsController extends Controller
|
||||
return view('locations/view', compact('location'));
|
||||
}
|
||||
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class ManufacturersController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
return view('manufacturers/index', compact('manufacturers'));
|
||||
return view('manufacturers/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -75,18 +75,7 @@ class ManufacturersController extends Controller
|
||||
$manufacturer->support_url = $request->input('support_url');
|
||||
$manufacturer->support_phone = $request->input('support_phone');
|
||||
$manufacturer->support_email = $request->input('support_email');
|
||||
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_slug($image->getClientOriginalName()).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/manufacturers/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$manufacturer->image = $file_name;
|
||||
}
|
||||
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
|
||||
|
||||
|
||||
|
||||
@@ -107,11 +96,14 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
// Handles manufacturer checks and permissions.
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($item = Manufacturer::find($id))) {
|
||||
if (!$item = Manufacturer::find($id)) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Show the page
|
||||
return view('manufacturers/edit', compact('item'));
|
||||
}
|
||||
@@ -129,7 +121,7 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $manufacturerId = null)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
|
||||
// Redirect to the manufacturer page
|
||||
@@ -142,37 +134,14 @@ class ManufacturersController extends Controller
|
||||
$manufacturer->support_url = $request->input('support_url');
|
||||
$manufacturer->support_phone = $request->input('support_phone');
|
||||
$manufacturer->support_email = $request->input('support_email');
|
||||
|
||||
$old_image = $manufacturer->image;
|
||||
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$manufacturer->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $manufacturer->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('manufacturers_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('manufacturers_upload_path'), $file_name);
|
||||
}
|
||||
$manufacturer->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('manufacturers_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
@@ -207,7 +176,7 @@ class ManufacturersController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +211,32 @@ class ManufacturersController extends Controller
|
||||
return redirect()->route('manufacturers.index')->with('error', $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a given Manufacturer (mark as un-deleted)
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.1.15]
|
||||
* @param int $manufacturers_id
|
||||
* @return Redirect
|
||||
*/
|
||||
public function restore($manufacturers_id)
|
||||
{
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::onlyTrashed()->where('id',$manufacturers_id)->first();
|
||||
|
||||
if ($manufacturer) {
|
||||
|
||||
// Not sure why this is necessary - it shouldn't fail validation here, but it fails without this, so....
|
||||
$manufacturer->setValidating(false);
|
||||
if ($manufacturer->restore()) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.restore.success'));
|
||||
}
|
||||
return redirect()->back()->with('error', 'Could not restore.');
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ class ModalController extends Controller
|
||||
}
|
||||
|
||||
function model() {
|
||||
return view('modals.model')
|
||||
->with('manufacturer', Helper::manufacturerList())
|
||||
->with('category', Helper::categoryList('asset'));
|
||||
return view('modals.model');
|
||||
}
|
||||
|
||||
function statuslabel() {
|
||||
|
||||
@@ -45,15 +45,24 @@ class ProfileController extends Controller
|
||||
{
|
||||
|
||||
$user = Auth::user();
|
||||
$user->first_name = Input::get('first_name');
|
||||
$user->last_name = Input::get('last_name');
|
||||
$user->website = Input::get('website');
|
||||
$user->location_id = Input::get('location_id');
|
||||
$user->gravatar = Input::get('gravatar');
|
||||
$user->locale = Input::get('locale');
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->website = $request->input('website');
|
||||
$user->gravatar = $request->input('gravatar');
|
||||
$user->phone = $request->input('phone');
|
||||
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en');
|
||||
}
|
||||
|
||||
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled=='1') && (!config('app.lock_passwords')))) {
|
||||
$user->two_factor_optin = Input::get('two_factor_optin', '0');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin', '0');
|
||||
}
|
||||
|
||||
if (Gate::allows('self.edit_location') && (!config('app.lock_passwords'))) {
|
||||
$user->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
if (Input::file('avatar')) {
|
||||
@@ -119,8 +128,7 @@ class ProfileController extends Controller
|
||||
|
||||
$rules = array(
|
||||
'current_password' => 'required',
|
||||
'password' => Setting::passwordComplexityRulesSaving('store'),
|
||||
'password_confirm' => 'required|same:password',
|
||||
'password' => Setting::passwordComplexityRulesSaving('store').'|confirmed',
|
||||
);
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
|
||||
@@ -26,6 +26,14 @@ use Illuminate\Http\Request;
|
||||
*/
|
||||
class ReportsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Checks for correct permissions
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->authorize('reports.view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the accessories report.
|
||||
@@ -82,136 +90,6 @@ class ReportsController extends Controller
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display asset report view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getAssetsReport()
|
||||
{
|
||||
$settings = \App\Models\Setting::first();
|
||||
return view('reports/asset', compact('assets'))->with('settings', $settings);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Exports the assets to CSV
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function exportAssetReport(Request $request)
|
||||
{
|
||||
|
||||
\Debugbar::disable();
|
||||
|
||||
$customfields = CustomField::get();
|
||||
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
|
||||
$assets = Asset::with('assignedTo', 'location','defaultLoc','assignedTo','model','supplier','assetstatus','model.manufacturer');
|
||||
|
||||
// This is used by the sidenav, mostly
|
||||
switch ($request->input('status')) {
|
||||
case 'Deleted':
|
||||
$assets->withTrashed()->Deleted();
|
||||
break;
|
||||
case 'Pending':
|
||||
$assets->Pending();
|
||||
break;
|
||||
case 'RTD':
|
||||
$assets->RTD();
|
||||
break;
|
||||
case 'Undeployable':
|
||||
$assets->Undeployable();
|
||||
break;
|
||||
case 'Archived':
|
||||
$assets->Archived();
|
||||
break;
|
||||
case 'Requestable':
|
||||
$assets->RequestableAssets();
|
||||
break;
|
||||
case 'Deployed':
|
||||
$assets->Deployed();
|
||||
break;
|
||||
}
|
||||
|
||||
$headers=[
|
||||
trans('general.company'),
|
||||
trans('admin/hardware/table.asset_tag'),
|
||||
trans('admin/hardware/form.manufacturer'),
|
||||
trans('general.category'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('general.model_no'),
|
||||
trans('general.name'),
|
||||
trans('admin/hardware/table.serial'),
|
||||
trans('general.status'),
|
||||
trans('admin/hardware/table.purchase_date'),
|
||||
trans('admin/hardware/table.purchase_cost'),
|
||||
trans('admin/hardware/form.order'),
|
||||
trans('general.supplier'),
|
||||
trans('admin/hardware/table.checkoutto'),
|
||||
trans('general.type'),
|
||||
trans('admin/hardware/table.checkout_date'),
|
||||
trans('admin/hardware/table.location'),
|
||||
trans('general.notes'),
|
||||
];
|
||||
foreach ($customfields as $field) {
|
||||
$headers[]=$field->name;
|
||||
}
|
||||
fputcsv($handle, $headers);
|
||||
|
||||
$assets->orderBy('created_at', 'DESC')->chunk(500, function($assets) use($handle, $customfields) {
|
||||
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
// Add a new row with data
|
||||
$values=[
|
||||
($asset->company) ? $asset->company->name : '',
|
||||
$asset->asset_tag,
|
||||
($asset->model->manufacturer) ? $asset->model->manufacturer->name : '',
|
||||
($asset->model->category) ? $asset->model->category->name : '',
|
||||
($asset->model) ? $asset->model->name : '',
|
||||
($asset->model->model_number) ? $asset->model->model_number : '',
|
||||
($asset->name) ? $asset->name : '',
|
||||
($asset->serial) ? $asset->serial : '',
|
||||
($asset->assetstatus) ? e($asset->present()->statusText) : '',
|
||||
($asset->purchase_date) ? e($asset->purchase_date) : '',
|
||||
($asset->purchase_cost > 0) ? Helper::formatCurrencyOutput($asset->purchase_cost) : '',
|
||||
($asset->order_number) ? e($asset->order_number) : '',
|
||||
($asset->supplier) ? e($asset->supplier->name) : '',
|
||||
($asset->checkedOutToUser() && $asset->assigned) ? e($asset->assigned->getFullNameAttribute()) : ($asset->assigned ? e($asset->assigned->display_name) : ''),
|
||||
($asset->checkedOutToUser() && $asset->assigned) ? 'user' : e($asset->assignedType()),
|
||||
($asset->last_checkout!='') ? e($asset->last_checkout) : '',
|
||||
($asset->location) ? e($asset->location->name) : '',
|
||||
($asset->notes) ? e($asset->notes) : '',
|
||||
];
|
||||
foreach ($customfields as $field) {
|
||||
$values[]=$asset->{$field->db_column_name()};
|
||||
}
|
||||
fputcsv($handle, $values);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the output stream
|
||||
fclose($handle);
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/csv',
|
||||
'Content-Disposition'
|
||||
=> 'attachment; filename="'.(($request->has('status')) ? trim($request->input('status')) : 'all').'-assets-'.date('Y-m-d-his').'.csv"',
|
||||
]);
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show depreciation report for assets.
|
||||
*
|
||||
@@ -429,123 +307,159 @@ class ReportsController extends Controller
|
||||
public function postCustom(Request $request)
|
||||
{
|
||||
|
||||
ini_set('max_execution_time', 12000);
|
||||
|
||||
|
||||
\Debugbar::disable();
|
||||
$customfields = CustomField::get();
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
|
||||
\Log::debug('Starting streamed response');
|
||||
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
stream_set_timeout($handle, 2000);
|
||||
|
||||
if ($request->has('use_bom')) {
|
||||
if ($request->filled('use_bom')) {
|
||||
fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
}
|
||||
|
||||
$header = [];
|
||||
|
||||
|
||||
if ($request->has('company')) {
|
||||
if ($request->filled('company')) {
|
||||
$header[] = trans('general.company');
|
||||
}
|
||||
|
||||
if ($request->has('asset_name')) {
|
||||
if ($request->filled('asset_name')) {
|
||||
$header[] = trans('admin/hardware/form.name');
|
||||
}
|
||||
|
||||
if ($request->has('asset_tag')) {
|
||||
if ($request->filled('asset_tag')) {
|
||||
$header[] = trans('admin/hardware/table.asset_tag');
|
||||
}
|
||||
|
||||
if ($request->has('model')) {
|
||||
if ($request->filled('model')) {
|
||||
$header[] = trans('admin/hardware/form.model');
|
||||
$header[] = trans('general.model_no');
|
||||
}
|
||||
|
||||
if ($request->has('category')) {
|
||||
if ($request->filled('category')) {
|
||||
$header[] = trans('general.category');
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer')) {
|
||||
if ($request->filled('manufacturer')) {
|
||||
$header[] = trans('admin/hardware/form.manufacturer');
|
||||
}
|
||||
|
||||
if ($request->has('serial')) {
|
||||
if ($request->filled('serial')) {
|
||||
$header[] = trans('admin/hardware/table.serial');
|
||||
}
|
||||
if ($request->has('purchase_date')) {
|
||||
if ($request->filled('purchase_date')) {
|
||||
$header[] = trans('admin/hardware/table.purchase_date');
|
||||
}
|
||||
|
||||
if (($request->has('purchase_cost')) || ($request->has('depreciation'))) {
|
||||
if (($request->filled('purchase_cost')) || ($request->filled('depreciation'))) {
|
||||
$header[] = trans('admin/hardware/table.purchase_cost');
|
||||
}
|
||||
|
||||
if ($request->has('eol')) {
|
||||
if ($request->filled('eol')) {
|
||||
$header[] = trans('admin/hardware/table.eol');
|
||||
}
|
||||
|
||||
if ($request->has('order')) {
|
||||
if ($request->filled('order')) {
|
||||
$header[] = trans('admin/hardware/form.order');
|
||||
}
|
||||
|
||||
if ($request->has('supplier')) {
|
||||
if ($request->filled('supplier')) {
|
||||
$header[] = trans('general.supplier');
|
||||
}
|
||||
|
||||
if ($request->has('location')) {
|
||||
if ($request->filled('location')) {
|
||||
$header[] = trans('admin/hardware/table.location');
|
||||
}
|
||||
if ($request->filled('location_address')) {
|
||||
$header[] = trans('general.address');
|
||||
$header[] = trans('general.address');
|
||||
$header[] = trans('general.city');
|
||||
$header[] = trans('general.state');
|
||||
$header[] = trans('general.country');
|
||||
$header[] = trans('general.zip');
|
||||
}
|
||||
|
||||
if ($request->has('assigned_to')) {
|
||||
if ($request->filled('rtd_location')) {
|
||||
$header[] = trans('admin/hardware/form.default_location');
|
||||
}
|
||||
|
||||
if ($request->filled('rtd_location_address')) {
|
||||
$header[] = trans('general.address');
|
||||
$header[] = trans('general.address');
|
||||
$header[] = trans('general.city');
|
||||
$header[] = trans('general.state');
|
||||
$header[] = trans('general.country');
|
||||
$header[] = trans('general.zip');
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('assigned_to')) {
|
||||
$header[] = trans('admin/hardware/table.checkoutto');
|
||||
$header[] = trans('general.type');
|
||||
}
|
||||
|
||||
if ($request->has('username')) {
|
||||
if ($request->filled('username')) {
|
||||
$header[] = 'Username';
|
||||
}
|
||||
|
||||
if ($request->has('employee_num')) {
|
||||
if ($request->filled('employee_num')) {
|
||||
$header[] = 'Employee No.';
|
||||
}
|
||||
|
||||
if ($request->has('status')) {
|
||||
if ($request->filled('manager')) {
|
||||
$header[] = trans('admin/users/table.manager');
|
||||
}
|
||||
|
||||
if ($request->filled('department')) {
|
||||
$header[] = trans('general.department');
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$header[] = trans('general.status');
|
||||
}
|
||||
|
||||
if ($request->has('warranty')) {
|
||||
if ($request->filled('warranty')) {
|
||||
$header[] = 'Warranty';
|
||||
$header[] = 'Warranty Expires';
|
||||
}
|
||||
if ($request->has('depreciation')) {
|
||||
if ($request->filled('depreciation')) {
|
||||
$header[] = 'Value';
|
||||
$header[] = 'Diff';
|
||||
}
|
||||
|
||||
if ($request->has('checkout_date')) {
|
||||
if ($request->filled('checkout_date')) {
|
||||
$header[] = trans('admin/hardware/table.checkout_date');
|
||||
}
|
||||
|
||||
if ($request->has('expected_checkin')) {
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$header[] = trans('admin/hardware/form.expected_checkin');
|
||||
}
|
||||
|
||||
if ($request->has('created_at')) {
|
||||
if ($request->filled('created_at')) {
|
||||
$header[] = trans('general.created_at');
|
||||
}
|
||||
|
||||
if ($request->has('updated_at')) {
|
||||
if ($request->filled('updated_at')) {
|
||||
$header[] = trans('general.updated_at');
|
||||
}
|
||||
|
||||
if ($request->has('last_audit_date')) {
|
||||
if ($request->filled('last_audit_date')) {
|
||||
$header[] = trans('general.last_audit');
|
||||
}
|
||||
|
||||
if ($request->has('next_audit_date')) {
|
||||
if ($request->filled('next_audit_date')) {
|
||||
$header[] = trans('general.next_audit_date');
|
||||
}
|
||||
|
||||
if ($request->has('notes')) {
|
||||
if ($request->filled('notes')) {
|
||||
$header[] = trans('general.notes');
|
||||
}
|
||||
|
||||
@@ -556,118 +470,158 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('Starting headers: '.$executionTime);
|
||||
fputcsv($handle, $header);
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('Added headers: '.$executionTime);
|
||||
|
||||
|
||||
$assets = \App\Models\Company::scopeCompanyables(Asset::select('assets.*'))->with(
|
||||
'location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer','supplier');
|
||||
|
||||
if ($request->has('by_location_id')) {
|
||||
if ($request->filled('by_location_id')) {
|
||||
$assets->where('assets.location_id', $request->input('by_location_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_supplier_id')) {
|
||||
if ($request->filled('by_rtd_location_id')) {
|
||||
\Log::debug('RTD location should match: '.$request->input('by_rtd_location_id'));
|
||||
$assets->where('assets.rtd_location_id', $request->input('by_rtd_location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_supplier_id')) {
|
||||
$assets->where('assets.supplier_id', $request->input('by_supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_company_id')) {
|
||||
if ($request->filled('by_company_id')) {
|
||||
$assets->where('assets.company_id', $request->input('by_company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_model_id')) {
|
||||
if ($request->filled('by_model_id')) {
|
||||
$assets->where('assets.model_id', $request->input('by_model_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_category_id')) {
|
||||
if ($request->filled('by_category_id')) {
|
||||
$assets->InCategory($request->input('by_category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_manufacturer_id')) {
|
||||
if ($request->filled('by_manufacturer_id')) {
|
||||
$assets->ByManufacturer($request->input('by_manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->has('by_order_number')) {
|
||||
if ($request->filled('by_order_number')) {
|
||||
$assets->where('assets.order_number', $request->input('by_order_number'));
|
||||
}
|
||||
|
||||
if ($request->has('by_status_id')) {
|
||||
if ($request->filled('by_status_id')) {
|
||||
$assets->where('assets.status_id', $request->input('by_status_id'));
|
||||
}
|
||||
|
||||
if (($request->has('purchase_start')) && ($request->has('purchase_end'))) {
|
||||
if (($request->filled('purchase_start')) && ($request->filled('purchase_end'))) {
|
||||
$assets->whereBetween('assets.purchase_date', [$request->input('purchase_start'), $request->input('purchase_end')]);
|
||||
}
|
||||
|
||||
if (($request->has('created_start')) && ($request->has('created_end'))) {
|
||||
if (($request->filled('created_start')) && ($request->filled('created_end'))) {
|
||||
$assets->whereBetween('assets.created_at', [$request->input('created_start'), $request->input('created_end')]);
|
||||
}
|
||||
|
||||
$assets->orderBy('assets.created_at', 'ASC')->chunk(500, function($assets) use($handle, $customfields, $request) {
|
||||
|
||||
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
}
|
||||
|
||||
$assets->orderBy('assets.created_at', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('Walking results: '.$executionTime);
|
||||
$count = 0;
|
||||
foreach ($assets as $asset) {
|
||||
$count++;
|
||||
$row = [];
|
||||
|
||||
if ($request->has('company')) {
|
||||
if ($request->filled('company')) {
|
||||
$row[] = ($asset->company) ? $asset->company->name : '';
|
||||
}
|
||||
|
||||
if ($request->has('asset_name')) {
|
||||
if ($request->filled('asset_name')) {
|
||||
$row[] = ($asset->name) ? $asset->name : '';
|
||||
}
|
||||
|
||||
if ($request->has('asset_tag')) {
|
||||
if ($request->filled('asset_tag')) {
|
||||
$row[] = ($asset->asset_tag) ? $asset->asset_tag : '';
|
||||
}
|
||||
|
||||
if ($request->has('model')) {
|
||||
if ($request->filled('model')) {
|
||||
$row[] = ($asset->model) ? $asset->model->name : '';
|
||||
$row[] = ($asset->model) ? $asset->model->model_number : '';
|
||||
}
|
||||
|
||||
if ($request->has('category')) {
|
||||
if ($request->filled('category')) {
|
||||
$row[] = (($asset->model) && ($asset->model->category)) ? $asset->model->category->name : '';
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer')) {
|
||||
if ($request->filled('manufacturer')) {
|
||||
$row[] = ($asset->model && $asset->model->manufacturer) ? $asset->model->manufacturer->name : '';
|
||||
}
|
||||
|
||||
if ($request->has('serial')) {
|
||||
if ($request->filled('serial')) {
|
||||
$row[] = ($asset->serial) ? $asset->serial : '';
|
||||
}
|
||||
|
||||
if ($request->has('purchase_date')) {
|
||||
if ($request->filled('purchase_date')) {
|
||||
$row[] = ($asset->purchase_date) ? $asset->purchase_date : '';
|
||||
}
|
||||
|
||||
if ($request->has('purchase_cost')) {
|
||||
if ($request->filled('purchase_cost')) {
|
||||
$row[] = ($asset->purchase_cost) ? Helper::formatCurrencyOutput($asset->purchase_cost) : '';
|
||||
}
|
||||
|
||||
if ($request->has('eol')) {
|
||||
if ($request->filled('eol')) {
|
||||
$row[] = ($asset->purchase_date!='') ? $asset->present()->eol_date() : '';
|
||||
}
|
||||
|
||||
if ($request->has('order')) {
|
||||
if ($request->filled('order')) {
|
||||
$row[] = ($asset->order_number) ? $asset->order_number : '';
|
||||
}
|
||||
|
||||
if ($request->has('supplier')) {
|
||||
if ($request->filled('supplier')) {
|
||||
$row[] = ($asset->supplier) ? $asset->supplier->name : '';
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('location')) {
|
||||
if ($request->filled('location')) {
|
||||
$row[] = ($asset->location) ? $asset->location->present()->name() : '';
|
||||
}
|
||||
|
||||
if ($request->has('assigned_to')) {
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? e($asset->assigned->getFullNameAttribute()) : ($asset->assigned ? e($asset->assigned->display_name) : '');
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : e($asset->assignedType());
|
||||
if ($request->filled('location_address')) {
|
||||
$row[] = ($asset->location) ? $asset->location->address : '';
|
||||
$row[] = ($asset->location) ? $asset->location->address2 : '';
|
||||
$row[] = ($asset->location) ? $asset->location->city : '';
|
||||
$row[] = ($asset->location) ? $asset->location->state : '';
|
||||
$row[] = ($asset->location) ? $asset->location->country : '';
|
||||
$row[] = ($asset->location) ? $asset->location->zip : '';
|
||||
}
|
||||
|
||||
if ($request->has('username')) {
|
||||
if ($request->filled('rtd_location')) {
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->present()->name() : '';
|
||||
}
|
||||
|
||||
if ($request->filled('rtd_location_address')) {
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address : '';
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address2 : '';
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->city : '';
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->state : '';
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->country : '';
|
||||
$row[] = ($asset->defaultLoc) ? $asset->defaultLoc->zip : '';
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('assigned_to')) {
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? $asset->assigned->getFullNameAttribute() : ($asset->assigned ? $asset->assigned->display_name : '');
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : $asset->assignedType();
|
||||
}
|
||||
|
||||
if ($request->filled('username')) {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->username : '';
|
||||
@@ -676,7 +630,7 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('employee_num')) {
|
||||
if ($request->filled('employee_num')) {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->employee_num : '';
|
||||
@@ -685,70 +639,94 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('status')) {
|
||||
if ($request->filled('manager')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = (($asset->assignedto) && ($asset->assignedto->manager)) ? $asset->assignedto->manager->present()->fullName : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('department')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = (($asset->assignedto) && ($asset->assignedto->department)) ? $asset->assignedto->department->name : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('warranty')) {
|
||||
if ($request->filled('warranty')) {
|
||||
$row[] = ($asset->warranty_months) ? $asset->warranty_months : '';
|
||||
$row[] = $asset->present()->warrantee_expires();
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('depreciation')) {
|
||||
if ($request->filled('depreciation')) {
|
||||
$depreciation = $asset->getDepreciatedValue();
|
||||
$diff = ($asset->purchase_cost - $depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($diff);
|
||||
}
|
||||
|
||||
if ($request->has('checkout_date')) {
|
||||
if ($request->filled('checkout_date')) {
|
||||
$row[] = ($asset->last_checkout) ? $asset->last_checkout : '';
|
||||
}
|
||||
|
||||
if ($request->has('expected_checkin')) {
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$row[] = ($asset->expected_checkin) ? $asset->expected_checkin : '';
|
||||
}
|
||||
|
||||
if ($request->has('created_at')) {
|
||||
if ($request->filled('created_at')) {
|
||||
$row[] = ($asset->created_at) ? $asset->created_at : '';
|
||||
}
|
||||
|
||||
if ($request->has('updated_at')) {
|
||||
if ($request->filled('updated_at')) {
|
||||
$row[] = ($asset->updated_at) ? $asset->updated_at : '';
|
||||
}
|
||||
|
||||
if ($request->has('last_audit_date')) {
|
||||
if ($request->filled('last_audit_date')) {
|
||||
$row[] = ($asset->last_audit_date) ? $asset->last_audit_date : '';
|
||||
}
|
||||
|
||||
if ($request->has('next_audit_date')) {
|
||||
if ($request->filled('next_audit_date')) {
|
||||
$row[] = ($asset->next_audit_date) ? $asset->next_audit_date : '';
|
||||
}
|
||||
|
||||
if ($request->has('notes')) {
|
||||
if ($request->filled('notes')) {
|
||||
$row[] = ($asset->notes) ? $asset->notes : '';
|
||||
}
|
||||
|
||||
foreach ($customfields as $customfield) {
|
||||
$column_name = $customfield->db_column_name();
|
||||
if ($request->has($customfield->db_column_name())) {
|
||||
if ($request->filled($customfield->db_column_name())) {
|
||||
$row[] = $asset->$column_name;
|
||||
}
|
||||
}
|
||||
fputcsv($handle, $row);
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('-- Record '.$count.' Asset ID:' .$asset->id. ' in '. $executionTime);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Close the output stream
|
||||
fclose($handle);
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('-- SCRIPT COMPLETED IN '. $executionTime);
|
||||
|
||||
}, 200, [
|
||||
'Content-Type' => 'text/csv',
|
||||
'Content-Disposition'
|
||||
=> 'attachment; filename="custom-assets-report-'.date('Y-m-d-his').'.csv"',
|
||||
]);
|
||||
|
||||
|
||||
return $response;
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -22,6 +23,7 @@ use App\Http\Requests\SetupUserRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SettingsLdapRequest;
|
||||
use App\Helpers\Helper;
|
||||
use App\Notifications\FirstAdminNotification;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Settings for
|
||||
@@ -57,9 +59,10 @@ class SettingsController extends Controller
|
||||
|
||||
$protocol = array_key_exists('HTTPS', $_SERVER) && ( $_SERVER['HTTPS'] == "on") ? 'https://' : 'http://';
|
||||
|
||||
$host = $_SERVER['SERVER_NAME'];
|
||||
if (($protocol === 'http://' && $_SERVER['SERVER_PORT'] != '80') || ($protocol === 'https://' && $_SERVER['SERVER_PORT'] != '443')) {
|
||||
$host .= ':' . $_SERVER['SERVER_PORT'];
|
||||
$host = array_key_exists('SERVER_NAME', $_SERVER) ? $_SERVER['SERVER_NAME'] : null;
|
||||
$port = array_key_exists('SERVER_PORT', $_SERVER) ? $_SERVER['SERVER_PORT'] : null;
|
||||
if (($protocol === 'http://' && $port != '80') || ($protocol === 'https://' && $port != '443')) {
|
||||
$host .= ':' . $port;
|
||||
}
|
||||
$pageURL = $protocol . $host . $_SERVER['REQUEST_URI'];
|
||||
|
||||
@@ -185,11 +188,19 @@ class SettingsController extends Controller
|
||||
$settings->save();
|
||||
|
||||
if (Input::get('email_creds')=='1') {
|
||||
Mail::send(['text' => 'emails.firstadmin'], $data, function ($m) use ($data) {
|
||||
$data = array();
|
||||
$data['email'] = $user->email;
|
||||
$data['username'] = $user->username;
|
||||
$data['first_name'] = $user->first_name;
|
||||
$data['last_name'] = $user->last_name;
|
||||
$data['password'] = $request->input('password');
|
||||
$user->notify(new FirstAdminNotification($data));
|
||||
|
||||
/*Mail::send(['text' => 'emails.firstadmin'], $data, function ($m) use ($data) {
|
||||
$m->to($data['email'], $data['first_name']);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.your_credentials'));
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
|
||||
@@ -316,7 +327,7 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->modellist_displays = '';
|
||||
|
||||
if (($request->has('show_in_model_list')) && (count($request->input('show_in_model_list')) > 0))
|
||||
if (($request->filled('show_in_model_list')) && (count($request->input('show_in_model_list')) > 0))
|
||||
{
|
||||
$setting->modellist_displays = implode(',', $request->input('show_in_model_list'));
|
||||
}
|
||||
@@ -324,6 +335,8 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
|
||||
$setting->load_remote = $request->input('load_remote', '0');
|
||||
$setting->unique_serial = $request->input('unique_serial', '0');
|
||||
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
|
||||
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
|
||||
$setting->dashboard_message = $request->input('dashboard_message');
|
||||
$setting->email_domain = $request->input('email_domain');
|
||||
@@ -336,6 +349,7 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->default_eula_text = $request->input('default_eula_text');
|
||||
$setting->thumbnail_max_h = $request->input('thumbnail_max_h');
|
||||
$setting->privacy_policy_link = $request->input('privacy_policy_link');
|
||||
|
||||
if (Input::get('per_page')!='') {
|
||||
$setting->per_page = $request->input('per_page');
|
||||
@@ -384,8 +398,12 @@ class SettingsController extends Controller
|
||||
$setting->brand = $request->input('brand', '1');
|
||||
$setting->header_color = $request->input('header_color');
|
||||
$setting->support_footer = $request->input('support_footer');
|
||||
$setting->version_footer = $request->input('version_footer');
|
||||
$setting->footer_text = $request->input('footer_text');
|
||||
$setting->skin = $request->input('skin');
|
||||
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
|
||||
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
|
||||
|
||||
|
||||
|
||||
// Only allow the site name and CSS to be changed if lock_passwords is false
|
||||
@@ -409,12 +427,23 @@ class SettingsController extends Controller
|
||||
$file_name = "logo.".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads');
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
|
||||
Image::make($image->getRealPath())->resize(null, 150, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
|
||||
// This is kinda copypasta from the ImageUploadRequest - should refactor the ImageUploadRequest to better handle maybe
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($image->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
file_put_contents($path.'/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
}
|
||||
$setting->logo = $file_name;
|
||||
}
|
||||
@@ -467,13 +496,18 @@ class SettingsController extends Controller
|
||||
$setting->two_factor_enabled = $request->input('two_factor_enabled');
|
||||
}
|
||||
|
||||
# remote user login
|
||||
$setting->login_remote_user_enabled = (int)$request->input('login_remote_user_enabled');
|
||||
$setting->login_common_disabled = (int)$request->input('login_common_disabled');
|
||||
$setting->login_remote_user_custom_logout_url = $request->input('login_remote_user_custom_logout_url');
|
||||
}
|
||||
|
||||
$setting->pwd_secure_uncommon = (int) $request->input('pwd_secure_uncommon');
|
||||
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
|
||||
$setting->pwd_secure_complexity = '';
|
||||
|
||||
if ($request->has('pwd_secure_complexity')) {
|
||||
|
||||
if ($request->filled('pwd_secure_complexity')) {
|
||||
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
|
||||
}
|
||||
|
||||
@@ -516,7 +550,9 @@ class SettingsController extends Controller
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->locale = $request->input('locale', 'en');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$setting->locale = $request->input('locale', 'en');
|
||||
}
|
||||
$setting->default_currency = $request->input('default_currency', '$');
|
||||
$setting->date_display_format = $request->input('date_display_format');
|
||||
$setting->time_display_format = $request->input('time_display_format');
|
||||
@@ -560,8 +596,11 @@ class SettingsController extends Controller
|
||||
|
||||
$alert_email = rtrim($request->input('alert_email'), ',');
|
||||
$alert_email = trim($alert_email);
|
||||
$admin_cc_email = rtrim($request->input('admin_cc_email'), ',');
|
||||
$admin_cc_email = trim($admin_cc_email);
|
||||
|
||||
$setting->alert_email = $alert_email;
|
||||
$setting->admin_cc_email = $admin_cc_email;
|
||||
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
|
||||
$setting->alert_interval = $request->input('alert_interval');
|
||||
$setting->alert_threshold = $request->input('alert_threshold');
|
||||
@@ -606,14 +645,24 @@ class SettingsController extends Controller
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->slack_endpoint = $request->input('slack_endpoint');
|
||||
$setting->slack_channel = $request->input('slack_channel');
|
||||
$setting->slack_botname = $request->input('slack_botname');
|
||||
$validatedData = $request->validate([
|
||||
'slack_endpoint' => 'url|required_with:slack_channel|nullable',
|
||||
'slack_channel' => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
|
||||
'slack_botname' => 'string|nullable',
|
||||
]);
|
||||
|
||||
if ($setting->save()) {
|
||||
if ($validatedData) {
|
||||
|
||||
$setting->slack_endpoint = $request->input('slack_endpoint');
|
||||
$setting->slack_channel = $request->input('slack_channel');
|
||||
$setting->slack_botname = $request->input('slack_botname');
|
||||
|
||||
$setting->save();
|
||||
return redirect()->route('settings.index')
|
||||
->with('success', trans('admin/settings/message.update.success'));
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($setting->getErrors());
|
||||
|
||||
}
|
||||
@@ -767,22 +816,34 @@ class SettingsController extends Controller
|
||||
|
||||
|
||||
|
||||
if (Input::has('labels_display_name')) {
|
||||
if ($request->filled('labels_display_name')) {
|
||||
$setting->labels_display_name = 1;
|
||||
} else {
|
||||
$setting->labels_display_name = 0;
|
||||
}
|
||||
|
||||
if (Input::has('labels_display_serial')) {
|
||||
if ($request->filled('labels_display_serial')) {
|
||||
$setting->labels_display_serial = 1;
|
||||
} else {
|
||||
$setting->labels_display_serial = 0;
|
||||
}
|
||||
|
||||
if (Input::has('labels_display_tag')) {
|
||||
if ($request->filled('labels_display_tag')) {
|
||||
$setting->labels_display_tag = 1;
|
||||
} else {
|
||||
$setting->labels_display_tag = 0;
|
||||
}
|
||||
|
||||
if ($request->filled('labels_display_tag')) {
|
||||
$setting->labels_display_tag = 1;
|
||||
} else {
|
||||
$setting->labels_display_tag = 0;
|
||||
}
|
||||
|
||||
if ($request->filled('labels_display_model')) {
|
||||
$setting->labels_display_model = 1;
|
||||
} else {
|
||||
$setting->labels_display_model = 0;
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
@@ -826,7 +887,7 @@ class SettingsController extends Controller
|
||||
$setting->ldap_server = $request->input('ldap_server');
|
||||
$setting->ldap_server_cert_ignore = $request->input('ldap_server_cert_ignore', false);
|
||||
$setting->ldap_uname = $request->input('ldap_uname');
|
||||
if (Input::has('ldap_pword')) {
|
||||
if (Input::filled('ldap_pword')) {
|
||||
$setting->ldap_pword = Crypt::encrypt($request->input('ldap_pword'));
|
||||
}
|
||||
$setting->ldap_basedn = $request->input('ldap_basedn');
|
||||
@@ -868,7 +929,7 @@ class SettingsController extends Controller
|
||||
public function getBackups()
|
||||
{
|
||||
|
||||
$path = storage_path().'/app/'.config('laravel-backup.backup.name');
|
||||
$path = storage_path().'/app/'.config('backup.backup.name');
|
||||
|
||||
$files = array();
|
||||
|
||||
@@ -944,7 +1005,7 @@ class SettingsController extends Controller
|
||||
public function downloadFile($filename = null)
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
$path = storage_path().'/app/'.config('laravel-backup.backup.name');
|
||||
$path = storage_path().'/app/'.config('backup.backup.name');
|
||||
$file = $path.'/'.$filename;
|
||||
if (file_exists($file)) {
|
||||
return Response::download($file);
|
||||
@@ -973,7 +1034,7 @@ class SettingsController extends Controller
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
$path = storage_path().'/app/'.config('laravel-backup.backup.name');
|
||||
$path = storage_path().'/app/'.config('backup.backup.name');
|
||||
$file = $path.'/'.$filename;
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
|
||||
@@ -33,7 +33,7 @@ class StatuslabelsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
return view('statuslabels.index', compact('statuslabels'));
|
||||
return view('statuslabels.index');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
@@ -43,7 +43,7 @@ class StatuslabelsController extends Controller
|
||||
return view('statuslabels.view')->with('statuslabel', $statuslabel);
|
||||
}
|
||||
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ class StatuslabelsController extends Controller
|
||||
// create a new model instance
|
||||
$statusLabel = new Statuslabel();
|
||||
|
||||
if (!$request->has('statuslabel_types')) {
|
||||
if (!$request->filled('statuslabel_types')) {
|
||||
return redirect()->back()->withInput()->withErrors(['statuslabel_types' => trans('validation.statuslabel_type')]);
|
||||
}
|
||||
|
||||
@@ -93,9 +93,9 @@ class StatuslabelsController extends Controller
|
||||
$statusLabel->archived = $statusType['archived'];
|
||||
$statusLabel->color = Input::get('color');
|
||||
$statusLabel->show_in_nav = Input::get('show_in_nav', 0);
|
||||
$statusLabel->default_label = Input::get('default_label', 0);
|
||||
|
||||
|
||||
// Was the asset created?
|
||||
if ($statusLabel->save()) {
|
||||
// Redirect to the new Statuslabel page
|
||||
return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.create.success'));
|
||||
@@ -103,36 +103,6 @@ class StatuslabelsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($statusLabel->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
$statuslabel = new Statuslabel();
|
||||
if (!$request->has('statuslabel_types')) {
|
||||
return JsonResponse::create(["error" => trans('validation.statuslabel_type')], 500);
|
||||
}
|
||||
$statustype = Statuslabel::getStatuslabelTypesForDB(Input::get('statuslabel_types'));
|
||||
$statuslabel->name = Input::get('name');
|
||||
$statuslabel->user_id = Auth::id();
|
||||
$statuslabel->notes = '';
|
||||
$statuslabel->deployable = $statustype['deployable'];
|
||||
$statuslabel->pending = $statustype['pending'];
|
||||
$statuslabel->archived = $statustype['archived'];
|
||||
|
||||
|
||||
if ($statuslabel->isValid()) {
|
||||
$statuslabel->save();
|
||||
// Redirect to the new Statuslabel page
|
||||
return JsonResponse::create($statuslabel);
|
||||
}
|
||||
return JsonResponse::create(["error" => $statuslabel->getErrors()->first()], 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Statuslabel update.
|
||||
*
|
||||
@@ -171,7 +141,7 @@ class StatuslabelsController extends Controller
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
|
||||
}
|
||||
|
||||
if (!$request->has('statuslabel_types')) {
|
||||
if (!$request->filled('statuslabel_types')) {
|
||||
return redirect()->back()->withInput()->withErrors(['statuslabel_types' => trans('validation.statuslabel_type')]);
|
||||
}
|
||||
|
||||
@@ -185,6 +155,7 @@ class StatuslabelsController extends Controller
|
||||
$statuslabel->archived = $statustype['archived'];
|
||||
$statuslabel->color = Input::get('color');
|
||||
$statuslabel->show_in_nav = Input::get('show_in_nav', 0);
|
||||
$statuslabel->default_label = Input::get('default_label', 0);
|
||||
|
||||
|
||||
// Was the asset created?
|
||||
|
||||
@@ -34,10 +34,9 @@ class SuppliersController extends Controller
|
||||
{
|
||||
// Grab all the suppliers
|
||||
$this->authorize('view', Supplier::class);
|
||||
$suppliers = Supplier::orderBy('created_at', 'DESC')->get();
|
||||
|
||||
// Show the page
|
||||
return view('suppliers/index', compact('suppliers'));
|
||||
return view('suppliers/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -79,17 +78,8 @@ class SuppliersController extends Controller
|
||||
$supplier->notes = request('notes');
|
||||
$supplier->url = $supplier->addhttp(request('url'));
|
||||
$supplier->user_id = Auth::id();
|
||||
$supplier = $request->handleImages($supplier,600, public_path().'/uploads/suppliers');
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/suppliers/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(200, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$supplier->image = $file_name;
|
||||
}
|
||||
|
||||
if ($supplier->save()) {
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.create.success'));
|
||||
@@ -97,23 +87,6 @@ class SuppliersController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($supplier->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Supplier::class);
|
||||
$supplier = new Supplier;
|
||||
$supplier->name = $request->input('name');
|
||||
$supplier->user_id = Auth::id();
|
||||
|
||||
if ($supplier->save()) {
|
||||
return JsonResponse::create($supplier);
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($supplier->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplier update.
|
||||
*
|
||||
@@ -163,39 +136,7 @@ class SuppliersController extends Controller
|
||||
$supplier->email = request('email');
|
||||
$supplier->url = $supplier->addhttp(request('url'));
|
||||
$supplier->notes = request('notes');
|
||||
|
||||
|
||||
$old_image = $supplier->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$supplier->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $supplier->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('suppliers_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('suppliers_upload_path'), $file_name);
|
||||
}
|
||||
$supplier->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('suppliers_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
$supplier = $request->handleImages($supplier,600, public_path().'/uploads/suppliers');
|
||||
|
||||
if ($supplier->save()) {
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.update.success'));
|
||||
@@ -214,7 +155,7 @@ class SuppliersController extends Controller
|
||||
public function destroy($supplierId)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
if (is_null($supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances','assets','licenses')->find($supplierId))) {
|
||||
if (is_null($supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances as asset_maintenances_count','assets as assets_count','licenses as licenses_count')->find($supplierId))) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
|
||||
}
|
||||
|
||||
@@ -254,11 +195,8 @@ class SuppliersController extends Controller
|
||||
if (isset($supplier->id)) {
|
||||
return view('suppliers/view', compact('supplier'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/suppliers/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('suppliers.index')->with('error', $error);
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.does_not_exist'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Group;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use App\Models\User;
|
||||
use App\Models\Group;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
use Artisan;
|
||||
use Auth;
|
||||
use Config;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use Gate;
|
||||
use HTML;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Input;
|
||||
use Lang;
|
||||
use League\Csv\Reader;
|
||||
@@ -29,11 +33,9 @@ use Redirect;
|
||||
use Response;
|
||||
use Str;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use URL;
|
||||
use View;
|
||||
use Illuminate\Http\Request;
|
||||
use Gate;
|
||||
use Artisan;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Users for
|
||||
@@ -84,8 +86,11 @@ class UsersController extends Controller
|
||||
$userPermissions = Helper::selectedPermissionsArray($permissions, Input::old('permissions', array()));
|
||||
$permissions = $this->filterDisplayable($permissions);
|
||||
|
||||
$user = new User;
|
||||
$user->activated = 1;
|
||||
|
||||
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
|
||||
->with('user', new User);
|
||||
->with('user', $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +107,7 @@ class UsersController extends Controller
|
||||
//Username, email, and password need to be handled specially because the need to respect config values on an edit.
|
||||
$user->email = $data['email'] = e($request->input('email'));
|
||||
$user->username = $data['username'] = e($request->input('username'));
|
||||
if ($request->has('password')) {
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
$data['password'] = $request->input('password');
|
||||
}
|
||||
@@ -110,7 +115,7 @@ class UsersController extends Controller
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', $user->activated);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle');
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
@@ -134,90 +139,35 @@ class UsersController extends Controller
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
if ($request->has('groups')) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
|
||||
if (($request->input('email_user') == 1) && ($request->has('email'))) {
|
||||
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
|
||||
// Send the credentials through email
|
||||
$data = array();
|
||||
$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'));
|
||||
|
||||
Mail::send('emails.send-login', $data, function ($m) use ($user) {
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
|
||||
/* Mail::send('emails.send-login', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.welcome', ['name' => $user->first_name]));
|
||||
});
|
||||
});*/
|
||||
}
|
||||
return redirect::route('users.index')->with('success', trans('admin/users/message.success.create'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON handler for creating a user through a modal popup
|
||||
*
|
||||
* @todo Handle validation more graciously
|
||||
* @author [B. Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return string JSON
|
||||
*/
|
||||
public function apiStore(SaveUserRequest $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = new User;
|
||||
$inputs = Input::except('csrf_token', 'password_confirm', 'groups', 'email_user');
|
||||
$inputs['activated'] = true;
|
||||
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->username = $request->input('username');
|
||||
$user->email = $request->input('email');
|
||||
$user->department_id = $request->input('department_id', null);
|
||||
if ($request->has('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
$user->activated = true;
|
||||
|
||||
// Was the user created?
|
||||
if ($user->save()) {
|
||||
|
||||
if (Input::get('email_user') == 1) {
|
||||
// Send the credentials through email
|
||||
$data = array();
|
||||
$data['email'] = $request->input('email');
|
||||
$data['first_name'] = $request->input('first_name');
|
||||
$data['last_name'] = $request->input('last_name');
|
||||
$data['password'] = $request->input('password');
|
||||
|
||||
Mail::send('emails.send-login', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.welcome', ['name' => $user->first_name]));
|
||||
});
|
||||
}
|
||||
|
||||
return JsonResponse::create($user);
|
||||
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: " . print_r($user->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the edit user form
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $permissions
|
||||
* @return View
|
||||
* @internal param int $id
|
||||
*/
|
||||
|
||||
private function filterDisplayable($permissions)
|
||||
{
|
||||
@@ -230,6 +180,15 @@ class UsersController extends Controller
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the edit user form
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $permissions
|
||||
* @return View
|
||||
* @internal param int $id
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
|
||||
@@ -291,23 +250,20 @@ class UsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
} catch (UserNotFoundException $e) {
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
|
||||
// Only save groups if the user is a super user
|
||||
if (Auth::user()->isSuperUser()) {
|
||||
if ($request->has('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('username')) {
|
||||
if ($request->filled('username')) {
|
||||
$user->username = $request->input('username');
|
||||
}
|
||||
$user->email = $request->input('email');
|
||||
@@ -319,9 +275,10 @@ class UsersController extends Controller
|
||||
$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', $user->activated);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle', null);
|
||||
$user->phone = $request->input('phone');
|
||||
$user->website = $request->input('website', null);
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
$user->company_id = Company::getIdForUser($request->input('company_id', null));
|
||||
$user->manager_id = $request->input('manager_id', null);
|
||||
@@ -331,6 +288,7 @@ class UsersController extends Controller
|
||||
$user->city = $request->input('city', null);
|
||||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
|
||||
|
||||
@@ -339,7 +297,7 @@ class UsersController extends Controller
|
||||
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
// Do we want to update the user password?
|
||||
if ($request->has('password')) {
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
@@ -375,32 +333,32 @@ class UsersController extends Controller
|
||||
{
|
||||
try {
|
||||
// Get user information
|
||||
$user = User::find($id);
|
||||
$user = User::findOrFail($id);
|
||||
// Authorize takes care of many of our logic checks now.
|
||||
$this->authorize('delete', User::class);
|
||||
|
||||
// Check if we are not trying to delete ourselves
|
||||
if ($user->id === Auth::user()->id) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
|
||||
return redirect()->route('users.index')->with('error', 'You cannot delete yourself.');
|
||||
}
|
||||
|
||||
if (count($user->assets) > 0) {
|
||||
if (($user->assets) && ($user->assets->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . count($user->assets) . ' assets associated with them.');
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets->count() . ' assets associated with them. Use the Checkin and Delete button on the user profile to check these items back in and delete this user.');
|
||||
}
|
||||
|
||||
if ($user->licenses()->count() > 0) {
|
||||
if (($user->licenses) && ($user->licenses->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->assets()->count() . ' assets associated with them.');
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->licenses->count() . ' license(s associated with them. Use the Checkin and Delete button on the user profile to check these items back in and delete this user.');
|
||||
}
|
||||
|
||||
if ($user->accessories()->count() > 0) {
|
||||
if (($user->accessories) && ($user->accessories->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->accessories()->count() . ' accessories associated with them.');
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->accessories->count() . ' accessories associated with them. Use the Checkin and Delete button on the user profile to check these items back in and delete this user.');
|
||||
}
|
||||
|
||||
if ($user->managedLocations()->count() > 0) {
|
||||
if (($user->managedLocations()) && ($user->managedLocations()->count() > 0)) {
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', 'This user still has ' . $user->managedLocations()->count() . ' locations that they manage.');
|
||||
}
|
||||
@@ -413,7 +371,7 @@ class UsersController extends Controller
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('success', $success);
|
||||
} catch (UserNotFoundException $e) {
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
// Redirect to the user management page
|
||||
@@ -431,23 +389,19 @@ class UsersController extends Controller
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
if ((!Input::has('ids')) || (count(Input::input('ids')) == 0)) {
|
||||
return redirect()->back()->with('error', 'No users selected');
|
||||
} else {
|
||||
|
||||
if (($request->filled('ids')) && (count($request->input('ids')) > 0)) {
|
||||
$statuslabel_list = Helper::statusLabelList();
|
||||
$user_raw_array = array_keys(Input::get('ids'));
|
||||
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
|
||||
$users = User::whereIn('id', $user_raw_array)->with('groups', 'assets', 'licenses', 'accessories')->get();
|
||||
if ($request->input('bulk_actions')=='edit') {
|
||||
|
||||
if ($request->input('bulk_actions') == 'edit') {
|
||||
return view('users/bulk-edit', compact('users'))
|
||||
->with('groups', Group::pluck('name', 'id'));
|
||||
}
|
||||
|
||||
return view('users/confirm-bulk-delete', compact('users', 'statuslabel_list'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'No users selected');
|
||||
}
|
||||
|
||||
|
||||
@@ -461,65 +415,66 @@ class UsersController extends Controller
|
||||
public function postBulkEditSave(Request $request)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
if ((!Input::has('ids')) || (count(Input::input('ids')) == 0)) {
|
||||
return redirect()->back()->with('error', 'No users selected');
|
||||
} else {
|
||||
|
||||
$user_raw_array = Input::get('ids');
|
||||
if (($request->filled('ids')) && (count($request->input('ids')) > 0)) {
|
||||
|
||||
$user_raw_array = $request->input('ids');
|
||||
$update_array = array();
|
||||
$manager_conflict = false;
|
||||
$users = User::whereIn('id', $user_raw_array)->where('id', '!=', Auth::user()->id)->get();
|
||||
|
||||
$users = User::whereIn('id', $user_raw_array)->where('id','!=',Auth::user()->id)->get();
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
if ($request->filled('location_id')) {
|
||||
$update_array['location_id'] = $request->input('location_id');
|
||||
}
|
||||
if ($request->has('department_id')) {
|
||||
if ($request->filled('department_id')) {
|
||||
$update_array['department_id'] = $request->input('department_id');
|
||||
}
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->filled('company_id')) {
|
||||
$update_array['company_id'] = $request->input('company_id');
|
||||
}
|
||||
if ($request->has('locale')) {
|
||||
if ($request->filled('locale')) {
|
||||
$update_array['locale'] = $request->input('locale');
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($request->has('manager_id')) {
|
||||
if ($request->filled('manager_id')) {
|
||||
|
||||
// Do not allow a manager update if the selected manager is one of the users being
|
||||
// edited.
|
||||
if (!array_key_exists($request->input('manager_id'), $user_raw_array)) {
|
||||
$update_array['manager_id'] = $request->input('manager_id');
|
||||
|
||||
} else {
|
||||
$manager_conflict = true;
|
||||
}
|
||||
|
||||
}
|
||||
if ($request->has('activated')) {
|
||||
if ($request->filled('activated')) {
|
||||
$update_array['activated'] = $request->input('activated');
|
||||
}
|
||||
|
||||
// Save the updated info
|
||||
if (count($update_array) > 0) {
|
||||
User::whereIn('id', $user_raw_array)->where('id','!=',Auth::user()->id)->update($update_array);
|
||||
User::whereIn('id', $user_raw_array)->where('id', '!=', Auth::user()->id)->update($update_array);
|
||||
}
|
||||
|
||||
// Only sync groups if groups were selected
|
||||
if ($request->has('groups')) {
|
||||
if ($request->filled('groups')) {
|
||||
foreach ($users as $user) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if ($manager_conflict) {
|
||||
if ($manager_conflict) {
|
||||
return redirect()->route('users.index')
|
||||
->with('warning', trans('admin/users/message.bulk_manager_warn'));
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')
|
||||
->with('warning', trans('admin/users/message.bulk_manager_warn'));
|
||||
->with('success', trans('admin/users/message.success.update_bulk'));
|
||||
}
|
||||
return redirect()->route('users.index')
|
||||
->with('success', trans('admin/users/message.success.update_bulk'));
|
||||
|
||||
return redirect()->back()->with('error', 'No users selected');
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -531,13 +486,13 @@ class UsersController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postBulkSave()
|
||||
public function postBulkSave(Request $request)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
if ((!Input::has('ids')) || (count(Input::has('ids')) == 0)) {
|
||||
if ((!$request->filled('ids')) || (count($request->input('ids')) == 0)) {
|
||||
return redirect()->back()->with('error', 'No users selected');
|
||||
} elseif ((!Input::has('status_id')) || (count(Input::has('status_id')) == 0)) {
|
||||
} elseif ((!$request->filled('status_id')) || ($request->input('status_id')=='')) {
|
||||
return redirect()->route('users.index')->with('error', 'No status selected');
|
||||
} else {
|
||||
|
||||
@@ -547,7 +502,7 @@ class UsersController extends Controller
|
||||
if (($key = array_search(Auth::user()->id, $user_raw_array)) !== false) {
|
||||
unset($user_raw_array[$key]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
@@ -651,10 +606,12 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function show($userId = null)
|
||||
{
|
||||
if(!$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($userId)) {
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
if (!$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')
|
||||
->withTrashed()
|
||||
->find($userId))
|
||||
{
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
|
||||
}
|
||||
|
||||
$userlog = $user->userlog->load('item');
|
||||
@@ -751,131 +708,11 @@ class UsersController extends Controller
|
||||
->with('userGroups', $userGroups)
|
||||
->with('clone_user', $user_to_clone);
|
||||
} catch (UserNotFoundException $e) {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user import view
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getImport()
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
// Selected groups
|
||||
$selectedGroups = Input::old('groups', array());
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$selectedPermissions = Input::old('permissions', array('superuser' => -1));
|
||||
// Show the page
|
||||
return view('users/import', compact('selectedGroups', 'permissions', 'selectedPermissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user import file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postImport()
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
if (!ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath(Input::file('user_import_csv'));
|
||||
$csv->setNewline("\r\n");
|
||||
|
||||
if (Input::get('has_headers') == 1) {
|
||||
$csv->setOffset(1);
|
||||
}
|
||||
|
||||
$duplicates = '';
|
||||
|
||||
$nbInsert = $csv->each(function ($row) use ($duplicates) {
|
||||
|
||||
if (array_key_exists(2, $row)) {
|
||||
|
||||
if (Input::get('activate') == 1) {
|
||||
$activated = '1';
|
||||
} else {
|
||||
$activated = '0';
|
||||
}
|
||||
|
||||
$pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 15);
|
||||
|
||||
// Location
|
||||
if (array_key_exists('4', $row)) {
|
||||
$user_location_id = trim($row[4]);
|
||||
if ($user_location_id=='') {
|
||||
$user_location_id = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// Check if this email already exists in the system
|
||||
$user = User::where('username', $row[2])->first();
|
||||
if ($user) {
|
||||
$duplicates .= $row[2] . ', ';
|
||||
} else {
|
||||
|
||||
$newuser = array(
|
||||
'first_name' => trim(e($row[0])),
|
||||
'last_name' => trim(e($row[1])),
|
||||
'username' => trim(e($row[2])),
|
||||
'email' => trim(e($row[3])),
|
||||
'password' => bcrypt($pass),
|
||||
'activated' => $activated,
|
||||
'location_id' => trim(e($user_location_id)),
|
||||
'phone' => trim(e($row[5])),
|
||||
'jobtitle' => trim(e($row[6])),
|
||||
'employee_num' => trim(e($row[7])),
|
||||
'company_id' => Company::getIdForUser($row[8]),
|
||||
'permissions' => '{"user":1}',
|
||||
'notes' => 'Imported user'
|
||||
);
|
||||
|
||||
DB::table('users')->insert($newuser);
|
||||
|
||||
|
||||
if (((Input::get('email_user') == 1) && !config('app.lock_passwords'))) {
|
||||
// Send the credentials through email
|
||||
if ($row[3] != '') {
|
||||
$data = array();
|
||||
$data['username'] = trim(e($row[2]));
|
||||
$data['first_name'] = trim(e($row[0]));
|
||||
$data['password'] = $pass;
|
||||
|
||||
if ($newuser['email']) {
|
||||
Mail::send('emails.send-login', $data, function ($m) use ($newuser) {
|
||||
$m->to($newuser['email'], $newuser['first_name'] . ' ' . $newuser['last_name']);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.welcome', ['name' => $newuser['first_name']]));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return redirect()->route('users.index')->with('duplicates', $duplicates)->with('success', 'Success');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return JSON response with a list of user details for the getIndex() view.
|
||||
*
|
||||
@@ -894,30 +731,38 @@ class UsersController extends Controller
|
||||
if (isset($user->id)) {
|
||||
$this->authorize('update', $user);
|
||||
|
||||
foreach (Input::file('file') as $file) {
|
||||
if (!$request->has('file')) {
|
||||
\Log::debug('No file selected: ');
|
||||
\Log::debug(print_r($request, true));
|
||||
return redirect()->back()->with('error', 'No file submitted.');
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'user-' . $user->id . '-' . str_random(8);
|
||||
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
|
||||
$upload_success = $file->move($destinationPath, $filename);
|
||||
} else {
|
||||
foreach ($request->file('file') as $file) {
|
||||
|
||||
//Log the uploaded file to the log
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->note = e(Input::get('notes'));
|
||||
$logAction->target_id = null;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->filename = $filename;
|
||||
$logAction->action_type = 'uploaded';
|
||||
$logAction->save();
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'user-' . $user->id . '-' . str_random(8);
|
||||
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
|
||||
$upload_success = $file->move($destinationPath, $filename);
|
||||
|
||||
//Log the uploaded file to the log
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->target_type = User::class;
|
||||
$logAction->target_id = $user->id;
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->note = $request->input('notes');
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->filename = $filename;
|
||||
$logAction->action_type = 'uploaded';
|
||||
$logAction->save();
|
||||
|
||||
}
|
||||
return redirect()->back()->with('success', 'File uploaded');
|
||||
}
|
||||
return JsonResponse::create($logAction);
|
||||
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($logAction->getErrors(), true)], 500);
|
||||
return redirect()->route('users.index')->with('error', 'Error uploading files');
|
||||
}
|
||||
|
||||
|
||||
@@ -945,10 +790,8 @@ class UsersController extends Controller
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.does_not_exist', compact('id'));
|
||||
// Redirect to the licence management page
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
@@ -1002,8 +845,7 @@ class UsersController extends Controller
|
||||
return redirect()->route('users.index')->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return view('users/ldap')
|
||||
->with('location_list', Helper::locationsList());
|
||||
return view('users/ldap');
|
||||
}
|
||||
|
||||
|
||||
@@ -1138,23 +980,6 @@ class UsersController extends Controller
|
||||
|
||||
}
|
||||
|
||||
public function postTwoFactorReset(Request $request)
|
||||
{
|
||||
if (Gate::denies('users.edit')) {
|
||||
return response()->json(['message' => trans('general.insufficient_permissions')], 500);
|
||||
}
|
||||
|
||||
try {
|
||||
$user = User::find($request->get('id'));
|
||||
$user->two_factor_secret = null;
|
||||
$user->two_factor_enrolled = 0;
|
||||
$user->save();
|
||||
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_success')], 200);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['message' => trans('admin/settings/general.two_factor_reset_error')], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP form processing.
|
||||
*
|
||||
@@ -1164,7 +989,7 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function printInventory($id)
|
||||
{
|
||||
|
||||
$this->authorize('view', User::class);
|
||||
$show_user = User::where('id',$id)->withTrashed()->first();
|
||||
$assets = Asset::where('assigned_to', $id)->where('assigned_type', User::class)->with('model', 'model.category')->get();
|
||||
$licenses = $show_user->licenses()->get();
|
||||
|
||||
@@ -12,6 +12,8 @@ use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\RequestAssetNotification;
|
||||
use App\Notifications\RequestAssetCancelationNotification;
|
||||
use Auth;
|
||||
use Config;
|
||||
use DB;
|
||||
@@ -59,7 +61,7 @@ class ViewAssetsController extends Controller
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users')->with('error', $error);
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +73,7 @@ class ViewAssetsController extends Controller
|
||||
$assets = Asset::with('model', 'defaultLoc', 'location', 'assignedTo', 'requests')->Hardware()->RequestableAssets()->get();
|
||||
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
|
||||
|
||||
return view('account/requestable-assets', compact('user', 'assets', 'models'));
|
||||
return view('account/requestable-assets', compact('assets', 'models'));
|
||||
}
|
||||
|
||||
|
||||
@@ -80,116 +82,71 @@ class ViewAssetsController extends Controller
|
||||
{
|
||||
$item = null;
|
||||
$fullItemType = 'App\\Models\\' . studly_case($itemType);
|
||||
|
||||
if ($itemType == "asset_model") {
|
||||
$itemType = "model";
|
||||
}
|
||||
$item = call_user_func(array($fullItemType, 'find'), $itemId);
|
||||
|
||||
$user = Auth::user();
|
||||
$quantity = $data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
|
||||
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $item->id;
|
||||
$logaction->item_type = $fullItemType;
|
||||
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
|
||||
|
||||
if ($user->location_id) {
|
||||
$logaction->location_id = $user->location_id;
|
||||
}
|
||||
$logaction->target_id = $data['user_id'] = Auth::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
|
||||
$data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
|
||||
$data['requested_by'] = $user->present()->fullName();
|
||||
$data['item_name'] = $item->name;
|
||||
$data['item'] = $item;
|
||||
$data['item_type'] = $itemType;
|
||||
$data['target'] = Auth::user();
|
||||
|
||||
|
||||
if ($fullItemType == Asset::class) {
|
||||
$data['item_url'] = route('hardware.show', $item->id);
|
||||
$slackMessage = ' Asset <'.url('/').'/hardware/'.$item->id.'/view'.'|'.$item->present()->name().'> requested by <'.url('/').'/users/'.$item->user_id.'/view'.'|'.$user->present()->fullName().'>.';
|
||||
} else {
|
||||
$data['item_url'] = route("view/${itemType}", $item->id);
|
||||
$slackMessage = $quantity. ' ' . class_basename(strtoupper($logaction->item_type)).' <'.$data['item_url'].'|'.$item->name.'> requested by <'.url('/').'/user/'.$item->id.'/view'.'|'.$user->present()->fullName().'>.';
|
||||
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$slackClient = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
}
|
||||
|
||||
if ($item->isRequestedBy($user)) {
|
||||
|
||||
$item->cancelRequest();
|
||||
$log = $logaction->logaction('request_canceled');
|
||||
if ($item_request = $item->isRequestedBy($user)) {
|
||||
$item->cancelRequest();
|
||||
$data['item_quantity'] = $item_request->qty;
|
||||
$logaction->logaction('request_canceled');
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-canceled', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Item_Request_Canceled'));
|
||||
});
|
||||
}
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
try {
|
||||
$slackClient->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'CANCELED:',
|
||||
'value' => $slackMessage
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Item Request Canceled');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
$settings->notify(new RequestAssetCancelationNotification($data));
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||
|
||||
} else {
|
||||
$item->request();
|
||||
|
||||
$log = $logaction->logaction('requested');
|
||||
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Item_Requested'));
|
||||
});
|
||||
$logaction->logaction('requested');
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
}
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
try {
|
||||
$slackClient->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'REQUESTED:',
|
||||
'value' => $slackMessage
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Item Requested');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function getRequestAsset($assetId = null)
|
||||
{
|
||||
|
||||
@@ -197,74 +154,47 @@ class ViewAssetsController extends Controller
|
||||
|
||||
// Check if the asset exists and is requestable
|
||||
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($asset)) {
|
||||
return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
// If it's requested, cancel the request.
|
||||
|
||||
$data['item'] = $asset;
|
||||
$data['target'] = Auth::user();
|
||||
$data['item_quantity'] = 1;
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = $data['item_type'] = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
|
||||
|
||||
if ($user->location_id) {
|
||||
$logaction->location_id = $user->location_id;
|
||||
}
|
||||
$logaction->target_id = $data['user_id'] = Auth::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
|
||||
|
||||
// If it's already requested, cancel the request.
|
||||
if ($asset->isRequestedBy(Auth::user())) {
|
||||
$asset->cancelRequest();
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
$asset->decrement('requests_counter', 1);
|
||||
|
||||
$logaction->logaction('request canceled');
|
||||
$settings->notify(new RequestAssetCancelationNotification($data));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('success')->with('success', trans('admin/hardware/message.requests.cancel-success'));
|
||||
} else {
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
|
||||
$data['asset_type'] = 'hardware';
|
||||
if ($user->location_id) {
|
||||
$logaction->location_id = $user->location_id;
|
||||
}
|
||||
$logaction->target_id = $data['user_id'] = Auth::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
$log = $logaction->logaction('requested');
|
||||
|
||||
$data['requested_by'] = $user->present()->fullName();
|
||||
$data['asset_name'] = $asset->present()->name();
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.asset_requested'));
|
||||
});
|
||||
}
|
||||
|
||||
$logaction->logaction('requested');
|
||||
$asset->request();
|
||||
$asset->increment('requests_counter', 1);
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'REQUESTED:',
|
||||
'value' => class_basename(strtoupper($logaction->item_type)).' asset <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.$asset->present()->name().'> requested by <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.Auth::user()->present()->fullName().'>.'
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Asset Requested');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
}
|
||||
|
||||
@@ -273,13 +203,10 @@ class ViewAssetsController extends Controller
|
||||
|
||||
public function getRequestedAssets()
|
||||
{
|
||||
$checkoutrequests = CheckoutRequest::all();
|
||||
|
||||
return view('account/requested-items', compact($checkoutrequests));
|
||||
return view('account/requested');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Get the acceptance screen
|
||||
public function getAcceptAsset($logID = null)
|
||||
{
|
||||
@@ -297,10 +224,12 @@ class ViewAssetsController extends Controller
|
||||
$user = Auth::user();
|
||||
|
||||
|
||||
if ($user->id != $findlog->item->assigned_to) {
|
||||
// TODO - Fix this for non-assets
|
||||
if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) {
|
||||
return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted'));
|
||||
}
|
||||
|
||||
|
||||
$item = $findlog->item;
|
||||
|
||||
// Check if the asset exists
|
||||
@@ -336,11 +265,11 @@ class ViewAssetsController extends Controller
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user->id != $findlog->item->assigned_to) {
|
||||
if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) {
|
||||
return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted'));
|
||||
}
|
||||
|
||||
if ($request->has('signature_output')) {
|
||||
if ($request->filled('signature_output')) {
|
||||
$path = config('app.private_uploads').'/signatures';
|
||||
$sig_filename = "siglog-".$findlog->id.'-'.date('Y-m-d-his').".png";
|
||||
$data_uri = e($request->get('signature_output'));
|
||||
@@ -388,9 +317,11 @@ class ViewAssetsController extends Controller
|
||||
->where('id', $findlog->id)
|
||||
->update(array('accepted_id' => $logaction->id));
|
||||
|
||||
if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) {
|
||||
$affected_asset = $logaction->item;
|
||||
$affected_asset->accepted = $accepted;
|
||||
$affected_asset->save();
|
||||
}
|
||||
|
||||
if ($update_checkout) {
|
||||
return redirect()->to('account/view-assets')->with('success', $return_msg);
|
||||
|
||||
@@ -22,10 +22,10 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\ReferrerPolicyHeader::class,
|
||||
\App\Http\Middleware\ContentSecurityPolicyHeader::class,
|
||||
\App\Http\Middleware\NosniffGuard::class,
|
||||
\App\Http\Middleware\CheckForSetup::class,
|
||||
\Fideloper\Proxy\TrustProxies::class,
|
||||
\App\Http\Middleware\CheckForSetup::class,
|
||||
\App\Http\Middleware\CheckForDebug::class,
|
||||
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,8 @@ class Kernel extends HttpKernel
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:60,1',
|
||||
\Barryvdh\Cors\HandleCors::class,
|
||||
'throttle:120,1',
|
||||
'auth:api',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -29,7 +29,7 @@ class CheckForSetup
|
||||
|
||||
} else {
|
||||
if (!($request->is('setup*')) && !($request->is('.env'))) {
|
||||
return redirect(url('/').'/setup')->with('Request', $request);
|
||||
return redirect(url('/').'/setup');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
||||
@@ -21,7 +21,7 @@ class ContentSecurityPolicyHeader
|
||||
|
||||
$policy[] = "default-src 'self'";
|
||||
$policy[] = "style-src 'self' 'unsafe-inline' oss.maxcdn.com";
|
||||
$policy[] = "script-src 'self' 'unsafe-inline' oss.mafxcdn.com cdnjs.cloudflare.com'";
|
||||
$policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval' cdnjs.cloudflare.com";
|
||||
$policy[] = "connect-src 'self'";
|
||||
$policy[] = "object-src 'none'";
|
||||
$policy[] = "font-src 'self' data:";
|
||||
|
||||
@@ -6,6 +6,8 @@ use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
|
||||
|
||||
class EncryptCookies extends BaseEncrypter
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
@@ -14,4 +16,13 @@ class EncryptCookies extends BaseEncrypter
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates if cookies should be serialized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $serialize = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ class AssetFileRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar|max:2000'
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,lic|max:'.$max_file_size,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\AssetModel;
|
||||
use Session;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
|
||||
class AssetRequest extends Request
|
||||
{
|
||||
@@ -30,7 +32,7 @@ class AssetRequest extends Request
|
||||
'model_id' => 'required|integer|exists:models,id',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'company_id' => 'integer|nullable',
|
||||
'warranty_months' => 'numeric|nullable',
|
||||
'warranty_months' => 'numeric|nullable|digits_between:0,240',
|
||||
'physical' => 'integer|nullable',
|
||||
'checkout_date' => 'date',
|
||||
'checkin_date' => 'date',
|
||||
@@ -46,22 +48,38 @@ class AssetRequest extends Request
|
||||
|
||||
$rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
|
||||
|
||||
$model = AssetModel::find($this->request->get('model_id'));
|
||||
if ($this->request->get('model_id') != '') {
|
||||
$model = AssetModel::find($this->request->get('model_id'));
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
$rules += $model->fieldset->validation_rules();
|
||||
if (($model) && (isset($model->fieldset)) && ($model->fieldset)) {
|
||||
$rules += $model->fieldset->validation_rules();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
|
||||
/**
|
||||
* Handle a failed validation attempt.
|
||||
*
|
||||
* public function json($data = [], $status = 200, array $headers = [], $options = 0)
|
||||
*
|
||||
* @param \Illuminate\Contracts\Validation\Validator $validator
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\HttpResponseException
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$this->session()->flash('errors', Session::get('errors', new \Illuminate\Support\ViewErrorBag)
|
||||
->put('default', new \Illuminate\Support\MessageBag($errors)));
|
||||
->put('default', new \Illuminate\Support\MessageBag($validator->errors()->toArray())));
|
||||
\Input::flash();
|
||||
return parent::response($errors);
|
||||
throw new HttpResponseException(response()->json([
|
||||
'status' => 'error',
|
||||
'messages' => $validator->errors(),
|
||||
'payload' => null
|
||||
], 422));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\SnipeModel;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
class ImageUploadRequest extends Request
|
||||
{
|
||||
@@ -24,8 +26,8 @@ class ImageUploadRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg|max:2000',
|
||||
'avatar' => 'mimes:png,gif,jpg,jpeg,svg|max:2000',
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg',
|
||||
'avatar' => 'mimes:png,gif,jpg,jpeg,svg',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -33,4 +35,83 @@ class ImageUploadRequest extends Request
|
||||
{
|
||||
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle and store any images attached to request
|
||||
* @param SnipeModel $item Item the image is associated with
|
||||
* @param String $path location for uploaded images, defaults to uploads/plural of item type.
|
||||
* @return SnipeModel Target asset is being checked out to.
|
||||
*/
|
||||
public function handleImages($item, $w = 600, $path = null)
|
||||
{
|
||||
|
||||
$type = strtolower(class_basename(get_class($item)));
|
||||
|
||||
if (is_null($path)) {
|
||||
$path = str_plural($type);
|
||||
}
|
||||
|
||||
\Log::debug('Trying to upload to '. $path);
|
||||
|
||||
if ($this->hasFile('image')) {
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
|
||||
if (!is_dir($path)) {
|
||||
\Log::debug($path.' does not exist');
|
||||
mkdir($path);
|
||||
}
|
||||
|
||||
$image = $this->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = $type.'-'.str_random(18).'.'.$ext;
|
||||
\Log::debug('File name will be: '.$file_name);
|
||||
|
||||
if ($image->getClientOriginalExtension()!=='svg') {
|
||||
\Log::debug('Not an SVG - resize');
|
||||
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
|
||||
$upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
\Log::debug('This is an SVG');
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($image->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
|
||||
file_put_contents($path.'/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove Current image if exists
|
||||
if (($item->image) && (file_exists($path.'/'.$item->image))) {
|
||||
try {
|
||||
unlink($path.'/'.$item->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
$item->image = $file_name;
|
||||
}
|
||||
|
||||
} elseif ($this->input('image_delete')=='1') {
|
||||
|
||||
try {
|
||||
unlink($path.'/'.$item->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
|
||||
$item->image = null;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,20 @@ class ItemImportRequest extends FormRequest
|
||||
$import->save();
|
||||
$fieldMappings=[];
|
||||
if ($import->field_map) {
|
||||
|
||||
// This checks to make sure the field header has been mapped.
|
||||
// If it hasn't been, it will throw an array_flip error
|
||||
foreach ($import->field_map as $field => $fieldValue) {
|
||||
$errorMessage = null;
|
||||
|
||||
if(is_null($fieldValue)){
|
||||
$errorMessage = 'All import fields must be mapped.';
|
||||
$this->errorCallback($import, $field, $errorMessage);
|
||||
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
|
||||
// We submit as csv field: column, but the importer is happier if we flip it here.
|
||||
$fieldMappings = array_change_key_case(array_flip($import->field_map), CASE_LOWER);
|
||||
// dd($fieldMappings);
|
||||
@@ -50,6 +64,7 @@ class ItemImportRequest extends FormRequest
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId(Auth::id())
|
||||
->setUpdating($this->has('import-update'))
|
||||
->setShouldNotify($this->has('send-welcome'))
|
||||
->setUsernameFormat('firstname.lastname')
|
||||
->setFieldMappings($fieldMappings);
|
||||
// $logFile = storage_path('logs/importer.log');
|
||||
@@ -60,7 +75,7 @@ class ItemImportRequest extends FormRequest
|
||||
|
||||
public function log($string)
|
||||
{
|
||||
// \Log::Info($string);
|
||||
\Log::Info($string);
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
|
||||
class SaveUserRequest extends Request
|
||||
class SaveUserRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
@@ -35,7 +37,10 @@ class SaveUserRequest extends Request
|
||||
{
|
||||
$rules['first_name'] = 'required|string|min:1';
|
||||
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('store');
|
||||
if ($this->request->get('ldap_import') == false)
|
||||
{
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('store').'|confirmed';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ class SaveUserRequest extends Request
|
||||
case 'PUT':
|
||||
$rules['first_name'] = 'required|string|min:1';
|
||||
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update').'|confirmed';
|
||||
break;
|
||||
|
||||
// Save only what's passed
|
||||
@@ -55,10 +60,9 @@ class SaveUserRequest extends Request
|
||||
|
||||
default:break;
|
||||
}
|
||||
|
||||
$rules['password_confirm'] = 'sometimes|required_with:password';
|
||||
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user