Compare commits
2486 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daf748e531 | ||
|
|
799a93c46a | ||
|
|
34aa12e229 | ||
|
|
897757bd04 | ||
|
|
c7125c3937 | ||
|
|
81a6332889 | ||
|
|
6e563f6e4b | ||
|
|
5320f5c67c | ||
|
|
7f69ae953b | ||
|
|
17f6fbabfa | ||
|
|
c79f8c1baf | ||
|
|
e7a820f7c9 | ||
|
|
12c92e30b7 | ||
|
|
fd10b755b0 | ||
|
|
dbbb7680d9 | ||
|
|
cf0dd5bbad | ||
|
|
25e53d8c7f | ||
|
|
89d433b41a | ||
|
|
e2570ada6f | ||
|
|
45afe725a1 | ||
|
|
536401fe0f | ||
|
|
ec6ed256fb | ||
|
|
2aaa7bed2d | ||
|
|
cc9f1577a4 | ||
|
|
ab1fe8be0c | ||
|
|
339bdddc38 | ||
|
|
35b9cf4b70 | ||
|
|
7ccb41371e | ||
|
|
2e60a457bf | ||
|
|
2390d2160b | ||
|
|
00b051b8c7 | ||
|
|
05b3a9ad7e | ||
|
|
4fb880384f | ||
|
|
43042ad841 | ||
|
|
a716382ac4 | ||
|
|
36c8f7f4f1 | ||
|
|
b42801f6ae | ||
|
|
946129f206 | ||
|
|
b941ef1e08 | ||
|
|
d1aa11ec89 | ||
|
|
de4934f21d | ||
|
|
b10076b015 | ||
|
|
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 | ||
|
|
42c8de5620 | ||
|
|
ba4f63bca3 | ||
|
|
4ed2ef6298 | ||
|
|
d28c2af7de | ||
|
|
1e8c32fbdb | ||
|
|
97bd87773c | ||
|
|
b560828173 | ||
|
|
0d7a43ab38 | ||
|
|
7865fbea7e | ||
|
|
5fcbf9091e | ||
|
|
fedd2b638c | ||
|
|
22f44e342f | ||
|
|
f7eb013935 | ||
|
|
1c28f893e7 | ||
|
|
195dfd752d | ||
|
|
cc788d3b62 | ||
|
|
e54021e7be | ||
|
|
918db39eba | ||
|
|
213fe2ecea | ||
|
|
6e70352d0b | ||
|
|
d2403cdadb | ||
|
|
9a4f5e0ba8 | ||
|
|
502e4d672a | ||
|
|
99205bf72e | ||
|
|
b3d673b0aa | ||
|
|
baf51eb430 | ||
|
|
d8fc50c351 | ||
|
|
ba5057181e | ||
|
|
4c4dee0bf1 | ||
|
|
589c3a2be3 | ||
|
|
991f182f89 | ||
|
|
f44b03f5a9 | ||
|
|
5fd50040d3 | ||
|
|
5302108556 | ||
|
|
592c62de75 | ||
|
|
27c1bc0271 | ||
|
|
3744a68daf | ||
|
|
c209b7bb5d | ||
|
|
f04493c1ab | ||
|
|
62edf14893 | ||
|
|
0d11e32523 | ||
|
|
5484b06df8 | ||
|
|
9f3116e4e3 | ||
|
|
f144d671ff | ||
|
|
b294635e17 | ||
|
|
36234cc0e8 | ||
|
|
5c87003196 | ||
|
|
cc5d36f8be | ||
|
|
1359a4f88a | ||
|
|
1e1362bdbc | ||
|
|
813106efd3 | ||
|
|
08d129c2ea | ||
|
|
8491e57258 | ||
|
|
d86fb098a8 | ||
|
|
e21f8e46d4 | ||
|
|
b3a6ec2804 | ||
|
|
0034938c04 | ||
|
|
d2587337e4 | ||
|
|
8b2045523d | ||
|
|
e7e10c24be | ||
|
|
f06852f48d | ||
|
|
2f9fff7130 | ||
|
|
0b788f64f7 | ||
|
|
facf1d42f7 | ||
|
|
e77aae703b | ||
|
|
76f52dd89b | ||
|
|
0e980f7229 | ||
|
|
2f5d550df6 | ||
|
|
3acf5f76fe | ||
|
|
4be8ae7f3d | ||
|
|
029dd43ecd | ||
|
|
cf8feb37e1 | ||
|
|
648531f4cc | ||
|
|
11505d5d06 | ||
|
|
1d04897b32 | ||
|
|
eddcc0fbbd | ||
|
|
bd80a77b78 | ||
|
|
edc30a31c1 | ||
|
|
f5636f325d | ||
|
|
426bf3803b | ||
|
|
739d3c72b7 | ||
|
|
60c38a0c47 | ||
|
|
9566fbd3cd | ||
|
|
e41ee1bcf8 | ||
|
|
bc14f225af | ||
|
|
07148c1503 | ||
|
|
fcb5eea951 | ||
|
|
7a4bf26733 | ||
|
|
2c2910d1dd | ||
|
|
1dae7a2fe4 | ||
|
|
e19ab329bd | ||
|
|
922cb90cb3 | ||
|
|
765295136a | ||
|
|
7579333ad3 | ||
|
|
543ea28b72 | ||
|
|
fefd6d60f6 | ||
|
|
a7b8b4bf55 | ||
|
|
7cafa194c1 | ||
|
|
b37f78dbbf | ||
|
|
e20cd42cc2 | ||
|
|
7b99f81f72 | ||
|
|
f1bbdc9e59 | ||
|
|
5219fb63a1 | ||
|
|
dcc379c3fa | ||
|
|
0fb8dc3418 | ||
|
|
f4e9d245d0 | ||
|
|
c75a2051ff | ||
|
|
691ec164b1 | ||
|
|
7b596c750d | ||
|
|
b346556caa | ||
|
|
93ec7068df | ||
|
|
5f65f993a0 | ||
|
|
a9441521a4 | ||
|
|
2dede67ae9 | ||
|
|
6bf17e7d47 | ||
|
|
a3240da707 | ||
|
|
a28fee1ff4 | ||
|
|
57e5af2f69 | ||
|
|
8d3a214475 | ||
|
|
58eedce6fe | ||
|
|
f91704a372 | ||
|
|
f53c68e040 | ||
|
|
99e55f84f0 | ||
|
|
798ea75f3d | ||
|
|
913e6a5709 | ||
|
|
34f6d5ab45 | ||
|
|
86c0194e9a | ||
|
|
dfb2b9b569 | ||
|
|
7fae380ab6 | ||
|
|
4ca8272efc | ||
|
|
5d4bbc393e | ||
|
|
984275ff05 | ||
|
|
e6f70f2ab7 | ||
|
|
f7de252f67 | ||
|
|
532b299c40 | ||
|
|
5dcc63dc74 | ||
|
|
6eb8acf319 | ||
|
|
862251ab36 | ||
|
|
95a9742571 | ||
|
|
81d0921782 | ||
|
|
0fa556da1d | ||
|
|
05bc238c9a | ||
|
|
782813ab49 | ||
|
|
cbca126d9d | ||
|
|
a9ffe8d210 | ||
|
|
c242abb42e | ||
|
|
2c722ffc4f | ||
|
|
484c47cef8 | ||
|
|
f38dc37152 | ||
|
|
6133f09056 | ||
|
|
2a959edeba | ||
|
|
8995d689b8 | ||
|
|
f08bec6c78 | ||
|
|
d920d91786 | ||
|
|
e35b3745eb | ||
|
|
e8252d9468 | ||
|
|
8fff6a1a06 | ||
|
|
0eff821928 | ||
|
|
9e605e0f79 | ||
|
|
8fb991110e | ||
|
|
b383ebee48 | ||
|
|
138313dcb9 | ||
|
|
f254958db9 | ||
|
|
09eff88679 | ||
|
|
24b356dba4 | ||
|
|
91bca5fcba | ||
|
|
96a469db36 | ||
|
|
9ab05e7037 | ||
|
|
52a99faf68 | ||
|
|
94cf1f8741 | ||
|
|
e2a8f9b790 | ||
|
|
409f5cc4fd | ||
|
|
3b5e4c44eb | ||
|
|
69a7ea63e2 | ||
|
|
aac379daeb | ||
|
|
1d3472b5c4 | ||
|
|
66a590b774 | ||
|
|
dc4472e9e9 | ||
|
|
6bfd428c2e | ||
|
|
f4623bd277 | ||
|
|
1f04d7aafd | ||
|
|
78efeec368 | ||
|
|
5b15a2fc0a | ||
|
|
68f1fa0340 | ||
|
|
9293f17707 | ||
|
|
a4b32e2328 | ||
|
|
d109ca30e2 | ||
|
|
3b4a651dd9 | ||
|
|
a4ac53e2e9 | ||
|
|
cf1b5a7685 | ||
|
|
0789eb8b07 | ||
|
|
7f674fdd35 | ||
|
|
500aa37e3c | ||
|
|
efe668d26d | ||
|
|
90db97b980 | ||
|
|
bc3d27fac6 | ||
|
|
55b9f1207d | ||
|
|
2bbb6001a8 | ||
|
|
af7b7664c5 | ||
|
|
c31362655c | ||
|
|
c6a956382f | ||
|
|
bab0bda174 | ||
|
|
bb52a8417c | ||
|
|
e3d7be23cb | ||
|
|
3d5545494e | ||
|
|
7f1a535b30 | ||
|
|
254234b0dc | ||
|
|
3566f37981 | ||
|
|
e6259eb6e1 | ||
|
|
eeb3d1eb42 | ||
|
|
79295f6434 | ||
|
|
285e4e2e52 | ||
|
|
5587b64d64 | ||
|
|
f0f2a5aa67 | ||
|
|
061317866b | ||
|
|
65ffc01583 | ||
|
|
f5f7a63a23 | ||
|
|
b6a7fd1cec | ||
|
|
6245f92e16 | ||
|
|
0abab2107c | ||
|
|
afc7116260 | ||
|
|
0b3d2b46e0 | ||
|
|
f786e07179 | ||
|
|
b2469bb34f | ||
|
|
b721bfcc84 | ||
|
|
f16ce09a7a | ||
|
|
bbe9df306b | ||
|
|
1bd7392531 | ||
|
|
2002dfca17 | ||
|
|
38c2ecbd0c | ||
|
|
ce97faa8d4 | ||
|
|
31ca4bff8c | ||
|
|
6f9033b2fd | ||
|
|
8864f81402 | ||
|
|
71ba1af647 | ||
|
|
b894a4c19a | ||
|
|
c3a44f25fd | ||
|
|
1f36e7997f | ||
|
|
33b5c26da5 | ||
|
|
e5a7e6619f | ||
|
|
d2e2c1c05f | ||
|
|
557b8b0ded | ||
|
|
37d4cf3afb | ||
|
|
b716db225f | ||
|
|
fbe093705d | ||
|
|
db278e9109 | ||
|
|
88798435f6 | ||
|
|
6220f0d8a5 | ||
|
|
30716b349b | ||
|
|
7a8c8233a2 | ||
|
|
7a2abcca33 | ||
|
|
9a40e5e651 | ||
|
|
893bcd0533 | ||
|
|
f1a911d305 | ||
|
|
8e8d9118e9 | ||
|
|
9c108873e9 | ||
|
|
6fe5d00e9b | ||
|
|
a85d5cfe92 | ||
|
|
6b257cc287 | ||
|
|
2952497a60 | ||
|
|
9018af4f1d | ||
|
|
5204e84703 | ||
|
|
55eaea0110 | ||
|
|
671a125c62 | ||
|
|
eb827cdbe6 | ||
|
|
5504dd435f | ||
|
|
608bb1b5b1 | ||
|
|
88b64034e8 | ||
|
|
694c6f3ec6 | ||
|
|
1d82f80e73 | ||
|
|
e21fa37254 | ||
|
|
1ef44721f5 | ||
|
|
528630a8d3 | ||
|
|
856a760d89 | ||
|
|
9179b6d9c4 | ||
|
|
4ce91a4f5d | ||
|
|
aff93d8f2b | ||
|
|
e4ab4024c5 | ||
|
|
612f23f6e0 | ||
|
|
1d543f83d4 | ||
|
|
88ca852dcf | ||
|
|
9fe2a7f81d | ||
|
|
a988011f0c | ||
|
|
c816870083 | ||
|
|
c5c46b7283 | ||
|
|
8bb8306f80 | ||
|
|
d91e8bfee5 | ||
|
|
2b3e5c8800 | ||
|
|
5b00a8ae33 | ||
|
|
5ee6e7f94b | ||
|
|
f90271dae5 | ||
|
|
e1423bd9d9 | ||
|
|
557714e7b7 | ||
|
|
3df62a200f | ||
|
|
a65ea639ed | ||
|
|
defed52caa | ||
|
|
035f7a5292 | ||
|
|
0ea3100896 | ||
|
|
2e0c2bd190 | ||
|
|
ceca76b344 | ||
|
|
a062769672 | ||
|
|
af6a794ae9 | ||
|
|
f4ac087c83 | ||
|
|
4898dd8e23 | ||
|
|
a090b6a9d2 | ||
|
|
023910472c | ||
|
|
25bf10b125 | ||
|
|
dfb0c09c51 | ||
|
|
41c8d6302a | ||
|
|
83547e3fed | ||
|
|
0d92ffc7ed | ||
|
|
3d0525bc51 | ||
|
|
808cd0f728 | ||
|
|
f5b3df697c | ||
|
|
29a36b5d1c | ||
|
|
ec131a7416 | ||
|
|
3da49ceb60 | ||
|
|
a19cef07bf | ||
|
|
c39e6d7006 | ||
|
|
60f6895919 | ||
|
|
bfa4812482 | ||
|
|
a083cdba18 | ||
|
|
438f484d68 | ||
|
|
f95d780fcf | ||
|
|
79fe092c81 | ||
|
|
027f6a7c12 | ||
|
|
76fe2af0af | ||
|
|
e490185533 | ||
|
|
deba4d2b81 | ||
|
|
a8cc29f062 | ||
|
|
c9e6a75ea8 | ||
|
|
7efa7ec03f | ||
|
|
9eef7a94ab | ||
|
|
244eceadec | ||
|
|
28fdbdd5d8 | ||
|
|
4584990cc3 | ||
|
|
4f3c932bb1 | ||
|
|
d7f2bceea2 | ||
|
|
18c1b2b477 | ||
|
|
174e3e720a | ||
|
|
fdaa279930 | ||
|
|
a4323a0308 | ||
|
|
ec4bed436c | ||
|
|
636c558fe6 | ||
|
|
29873f9c22 | ||
|
|
912ee20f3c | ||
|
|
9f10080243 | ||
|
|
b3c386663f | ||
|
|
f2d25ff777 | ||
|
|
dec9d959db | ||
|
|
2aafdb1400 | ||
|
|
c6b02cdc02 | ||
|
|
f0c825a9b3 | ||
|
|
79c035da11 | ||
|
|
9deafd771e | ||
|
|
cbc09f3a12 | ||
|
|
22c4d79cfb | ||
|
|
71b9a15c9c | ||
|
|
db328e1ce5 | ||
|
|
8b4c85d69a | ||
|
|
78a51d3675 | ||
|
|
a5bcf53146 | ||
|
|
9f97b4aefd | ||
|
|
62468199af | ||
|
|
c220315cb0 | ||
|
|
5a3233da37 | ||
|
|
80109071a2 | ||
|
|
c43bb670d2 | ||
|
|
14874d8e8a | ||
|
|
ff793f1cb5 | ||
|
|
500f6d7baf | ||
|
|
ee9a229c0e | ||
|
|
8140bdaa88 | ||
|
|
5979a18852 | ||
|
|
5efb803b60 | ||
|
|
fd4a8edae9 | ||
|
|
e9fdf06bf6 | ||
|
|
0a5b72e71e | ||
|
|
51168e8e10 | ||
|
|
cefdca3d22 | ||
|
|
cc5eee1890 | ||
|
|
d3864db5e1 | ||
|
|
ece8ae3adc | ||
|
|
e25829c759 | ||
|
|
92afd5f232 | ||
|
|
b934d2e504 | ||
|
|
8cf70e7e20 | ||
|
|
cdfd720c65 | ||
|
|
c0f791cf13 | ||
|
|
290cf79778 | ||
|
|
8af1481749 | ||
|
|
d8f404096c | ||
|
|
ea2f7617df | ||
|
|
b6c258bb12 | ||
|
|
804b49cefb | ||
|
|
305b0d8edb | ||
|
|
05996019e5 | ||
|
|
31a967e072 | ||
|
|
0b56ebf291 | ||
|
|
fcc87b3219 | ||
|
|
82fca0c72d | ||
|
|
51661b0a21 | ||
|
|
e8670fe591 | ||
|
|
bee1dfc4a6 | ||
|
|
83c8449aca | ||
|
|
9dba1bb3e5 | ||
|
|
76e3398d44 | ||
|
|
a7e12931fa | ||
|
|
ba04c64567 | ||
|
|
8c8352ecc6 | ||
|
|
64b670033d | ||
|
|
b9d102a5fb | ||
|
|
77076e02e8 | ||
|
|
c6a761a5ad | ||
|
|
6f3a90c48b | ||
|
|
8f160a8590 | ||
|
|
2278d5bfd8 | ||
|
|
66ac147c9e | ||
|
|
05e0d15ae4 | ||
|
|
8562f018ed | ||
|
|
0cbdcce3ea | ||
|
|
980be65193 | ||
|
|
3aaaea37e4 | ||
|
|
964c594c4c | ||
|
|
9430c4bf43 | ||
|
|
538757317b | ||
|
|
fe986f7c51 | ||
|
|
09105871d9 | ||
|
|
e84a6059f4 | ||
|
|
09f20873df | ||
|
|
2adc1e8ba9 | ||
|
|
4a6c18532b | ||
|
|
921f882680 | ||
|
|
f79e5add58 | ||
|
|
d98d06377e | ||
|
|
22fdd05314 | ||
|
|
8c15a4e0c6 | ||
|
|
9250b45e6e | ||
|
|
3a1de3d2a5 | ||
|
|
b59dd11304 | ||
|
|
b6222abb7c | ||
|
|
dcf8e4f5ef | ||
|
|
f195073ac3 | ||
|
|
452a9d6725 | ||
|
|
1d74ddc547 | ||
|
|
932b589a14 | ||
|
|
671e514785 | ||
|
|
1e2ebdb69c | ||
|
|
148751d927 | ||
|
|
efecdfaea0 | ||
|
|
ab9c84a6b6 | ||
|
|
8711bc0dbd | ||
|
|
0adebd1ec8 | ||
|
|
43c1e893c0 | ||
|
|
7ce63e653b | ||
|
|
e5129a8b98 | ||
|
|
a922c1a298 | ||
|
|
f4aa812d96 | ||
|
|
0c9e41e1fa | ||
|
|
1d6320a88f | ||
|
|
4696e799ed | ||
|
|
2e08a91c53 | ||
|
|
4fd35573ad | ||
|
|
caf2cdbf6f | ||
|
|
c0293a7c1c | ||
|
|
17405f5de1 | ||
|
|
bfefa10462 | ||
|
|
55a140045b | ||
|
|
897bd2c56e | ||
|
|
7321c5937f | ||
|
|
98700cab8b | ||
|
|
3d07635820 | ||
|
|
59b18a6ada | ||
|
|
ce525c1985 | ||
|
|
a0d163a3c4 | ||
|
|
faab971931 | ||
|
|
38eb16dfea | ||
|
|
4494456cad | ||
|
|
876dde1280 | ||
|
|
17ee904828 | ||
|
|
4f1b58cd35 | ||
|
|
edcd3afc3e | ||
|
|
e433b0f9d8 | ||
|
|
f137e516a6 | ||
|
|
ba38b841cb | ||
|
|
f7c6697a69 | ||
|
|
498fc3762d | ||
|
|
eb24eb1fff | ||
|
|
3e5e6ba99a | ||
|
|
0396267388 | ||
|
|
4577ba39d7 | ||
|
|
7eef1b4bcf | ||
|
|
578da128e9 | ||
|
|
bb4d49690f | ||
|
|
7eb94d16a1 | ||
|
|
65bd33c274 | ||
|
|
0f7aa21a59 | ||
|
|
ee0814716a | ||
|
|
f56e1768b6 | ||
|
|
012afe99e2 | ||
|
|
0c02b6d24e | ||
|
|
2f34336f2b | ||
|
|
afe6f43a1b | ||
|
|
738e33b165 | ||
|
|
566fd4d2e1 | ||
|
|
c61887da21 | ||
|
|
6852b74317 | ||
|
|
4c266fdcf4 | ||
|
|
c26a2f8291 | ||
|
|
a994e54726 | ||
|
|
172c7c75a8 | ||
|
|
487fd17ce3 | ||
|
|
65353fa422 | ||
|
|
4f4920615c | ||
|
|
c162c02304 | ||
|
|
1bb1480f67 | ||
|
|
8dceacc2b4 | ||
|
|
ffae537400 | ||
|
|
5ce7882bf5 | ||
|
|
b36594f508 | ||
|
|
59c9c22a59 | ||
|
|
9097681c13 | ||
|
|
567f741d95 | ||
|
|
9b1fb90519 | ||
|
|
b567ffdcfe | ||
|
|
9d44607b8f | ||
|
|
35ee52212f | ||
|
|
e9e32fdb00 | ||
|
|
6c130ce8ac | ||
|
|
dd7db0de93 | ||
|
|
70efac8fa7 | ||
|
|
6e2556eefd | ||
|
|
fb6a545cc6 | ||
|
|
e5c1e41966 | ||
|
|
61617a2629 | ||
|
|
a3e80882c1 | ||
|
|
31980c55de | ||
|
|
d1022e8ff7 | ||
|
|
e6ff447ee8 | ||
|
|
1123272ae8 | ||
|
|
74aa562a3b | ||
|
|
74773ac912 | ||
|
|
9764d2ad24 | ||
|
|
d03b8c6528 | ||
|
|
99a355145e | ||
|
|
7233e2dded | ||
|
|
eed50112d5 | ||
|
|
5aee5a3f3d | ||
|
|
f21c7ba312 | ||
|
|
9d7455f022 | ||
|
|
b748e7ed5e | ||
|
|
84a717c6ad | ||
|
|
a202e1657c | ||
|
|
30ec919048 | ||
|
|
4ae4083b7b | ||
|
|
dec9ac1ac8 | ||
|
|
8776d28d3b | ||
|
|
231dea0ebc | ||
|
|
cceeb5c8a2 | ||
|
|
579334b5fc | ||
|
|
3a82fbe714 | ||
|
|
143071fa0c | ||
|
|
0589652edb | ||
|
|
b772d8e527 | ||
|
|
b96d4dcf1f | ||
|
|
dc32e4bdb0 | ||
|
|
43f2a530f5 | ||
|
|
6ece593629 | ||
|
|
33de0ec8a9 | ||
|
|
8e17714c12 | ||
|
|
32fc052c3b | ||
|
|
17febca466 | ||
|
|
8572a9771b | ||
|
|
3ea294c0e6 | ||
|
|
2b3b2e3197 | ||
|
|
a9d9234fb3 | ||
|
|
a11d9b84a4 | ||
|
|
936ff707c7 | ||
|
|
def69b1aaa | ||
|
|
4818e1b8ca | ||
|
|
623f21e51f | ||
|
|
bd524ff2f3 | ||
|
|
04ab522ee3 | ||
|
|
f672b14468 | ||
|
|
b0973de1a6 | ||
|
|
fe06ef10f1 | ||
|
|
14ca690441 | ||
|
|
2de0a3669e | ||
|
|
c7c63e8432 | ||
|
|
adf6afbb43 | ||
|
|
69b90fb65e | ||
|
|
97ea68b15c | ||
|
|
badc763c06 | ||
|
|
b6a14d2c9c | ||
|
|
d59dd0f636 | ||
|
|
d68d95a915 | ||
|
|
15d4344efb | ||
|
|
dc10f18188 | ||
|
|
2522bfee9c | ||
|
|
d7f8615964 | ||
|
|
88dff754b1 | ||
|
|
ecd21074fb | ||
|
|
12caa48390 | ||
|
|
cc7be5f947 | ||
|
|
5b489e003d | ||
|
|
8c1f4b006e | ||
|
|
3a52c19428 | ||
|
|
279ad6d80a | ||
|
|
b786791401 | ||
|
|
1c12b6e13b | ||
|
|
c06539dee3 | ||
|
|
3b9544d1f3 | ||
|
|
da9bb07041 | ||
|
|
5d9b9ad590 | ||
|
|
f95502ae35 | ||
|
|
877daba096 | ||
|
|
b3b8ab493e | ||
|
|
1859c8f5ab | ||
|
|
87ba042b2d | ||
|
|
6fb0ef908d | ||
|
|
c4a30cc646 | ||
|
|
cf56f70b3a | ||
|
|
f84e6a34cc | ||
|
|
85360a7c7f | ||
|
|
9e0b3afa91 | ||
|
|
416455fe01 | ||
|
|
53a1511cac | ||
|
|
733921f1f9 | ||
|
|
3ecaa99990 | ||
|
|
ab9729c39a | ||
|
|
104cc2bf11 | ||
|
|
1659c3f1a6 | ||
|
|
caa8ec3178 | ||
|
|
0c794c103b | ||
|
|
53175d5035 | ||
|
|
0bd09f9c46 | ||
|
|
27d795508d | ||
|
|
368ac5b85d | ||
|
|
d5635f32e5 | ||
|
|
f47075c180 | ||
|
|
0a5e4b9b7b | ||
|
|
85624205b4 | ||
|
|
2330e5ee57 | ||
|
|
c9c5ce6ee0 | ||
|
|
fc3a59d193 | ||
|
|
950519be5d | ||
|
|
e64cf8b320 | ||
|
|
87affa40ed | ||
|
|
513a1b1e3b | ||
|
|
cf09908c60 | ||
|
|
d21c92f91b | ||
|
|
ffd93c59d6 | ||
|
|
01bb4bf64a | ||
|
|
28a4293a0b | ||
|
|
f095f1807c | ||
|
|
e08911ab8f | ||
|
|
ca6dc5c2b5 | ||
|
|
27875c2dac | ||
|
|
6f886d3d6e | ||
|
|
fd74e4308b | ||
|
|
3695e118f4 | ||
|
|
f43692938b | ||
|
|
6008eec205 | ||
|
|
3343cf16dd | ||
|
|
48207fc695 | ||
|
|
3dae464c34 | ||
|
|
0c524e0830 | ||
|
|
da56a253bc | ||
|
|
a844d5b018 | ||
|
|
ba9bb470eb | ||
|
|
41452450b3 | ||
|
|
a9e5ad0df1 | ||
|
|
0e2f4f3cfb | ||
|
|
3bc9d3f3f1 | ||
|
|
81ca0ac91d | ||
|
|
3ca5d39c66 | ||
|
|
4f008e118f | ||
|
|
e11f9313f0 | ||
|
|
d379c6b61f | ||
|
|
d36e8cfbd2 | ||
|
|
4cdcbc97ee | ||
|
|
ba516ac9af | ||
|
|
feb2f5b076 | ||
|
|
b5f1e10b45 | ||
|
|
a1eac967a7 | ||
|
|
6186c324b5 | ||
|
|
772785f9b5 | ||
|
|
2f6c0cee59 | ||
|
|
e56f1ee6fd | ||
|
|
37868cd70e | ||
|
|
32b2f77ad9 | ||
|
|
472a5b9f69 | ||
|
|
121e158f39 | ||
|
|
f4e7bfc28d | ||
|
|
0089f73686 | ||
|
|
0f4c05c5d0 | ||
|
|
379274deff | ||
|
|
dbf5fec7b0 | ||
|
|
4bb546a882 | ||
|
|
7f1b7be416 | ||
|
|
02720f225c | ||
|
|
e44e573a3c | ||
|
|
546c3e50fa | ||
|
|
7f1b962e56 | ||
|
|
4eee7f8d97 | ||
|
|
0fd0e3a8b4 | ||
|
|
1076ec96be | ||
|
|
3b498efee1 | ||
|
|
9687a78981 | ||
|
|
2244a4b3cf | ||
|
|
f3f84f1a8c | ||
|
|
41994c95e0 | ||
|
|
39c68214e9 | ||
|
|
9c94e70917 | ||
|
|
6a3716a06d | ||
|
|
feccc55c54 | ||
|
|
fe70792cbd | ||
|
|
95b6e0d2d8 | ||
|
|
5d890fb139 | ||
|
|
cd2816b1c7 | ||
|
|
2172e6cc25 | ||
|
|
04130a568c | ||
|
|
108ac79442 | ||
|
|
3d7fd5cf04 | ||
|
|
5737de2e22 | ||
|
|
1e21cef218 | ||
|
|
ad7a2da9bd | ||
|
|
bd48ae96c2 | ||
|
|
0f5e0dcd4f | ||
|
|
c37fa44f72 | ||
|
|
daaf98783f | ||
|
|
1399ebb133 | ||
|
|
03f6211582 | ||
|
|
20bcc73000 | ||
|
|
0058f02e82 | ||
|
|
25b8c4438e | ||
|
|
4f1747023a | ||
|
|
46fb5c9d40 | ||
|
|
52f10232a1 | ||
|
|
5278dac2b0 | ||
|
|
890012f6c4 | ||
|
|
3991f79115 | ||
|
|
0a114c7daf | ||
|
|
3064b3f80e | ||
|
|
df430a2263 | ||
|
|
277e49468b | ||
|
|
f687c8db24 | ||
|
|
c616041876 | ||
|
|
d76f858dcd | ||
|
|
7a543fa6d5 | ||
|
|
49afd325a9 | ||
|
|
ce5ccc31f0 | ||
|
|
c70db75de9 | ||
|
|
7b76bbfd68 | ||
|
|
cfd1925625 | ||
|
|
e8b4bdf6f4 | ||
|
|
7e0c33d535 | ||
|
|
24211cb674 | ||
|
|
2d758be0e1 | ||
|
|
f49ecbdb61 | ||
|
|
3cea12565b | ||
|
|
b1ac024725 | ||
|
|
ec68bd7842 | ||
|
|
a224904ade | ||
|
|
cb3b294baa | ||
|
|
0788347990 | ||
|
|
7496a902bd | ||
|
|
f2d04be8fe | ||
|
|
f2499fc7d2 | ||
|
|
00d910ddbc | ||
|
|
c6d191bcba | ||
|
|
f13836eb55 | ||
|
|
76c4c19b3e | ||
|
|
1d212b59bd | ||
|
|
1174d37c20 | ||
|
|
6bcb55a129 | ||
|
|
a99e09e5e3 | ||
|
|
7d11cb0748 | ||
|
|
9c29ee9c6d | ||
|
|
bdb95e4e3d | ||
|
|
1fa6228fb7 | ||
|
|
8b535c1806 | ||
|
|
b71d0ab484 | ||
|
|
ea07517ad5 | ||
|
|
82690e1fd7 | ||
|
|
75b527ab59 | ||
|
|
b083541723 | ||
|
|
6dbb598616 | ||
|
|
e8d938e188 | ||
|
|
f7c92f61e1 | ||
|
|
4f80eac467 | ||
|
|
0e5af78cf1 | ||
|
|
0d34cc704a | ||
|
|
d4bb4d2edd | ||
|
|
d008334f2d | ||
|
|
109ea82cb9 | ||
|
|
e8847753f4 | ||
|
|
7dad71d2b6 | ||
|
|
db5968f95a | ||
|
|
d97a1edeb4 | ||
|
|
3dd39a46be | ||
|
|
dc9a908de7 | ||
|
|
687cf44d3d | ||
|
|
e054504669 | ||
|
|
e1ad28aa20 | ||
|
|
87992b7f71 | ||
|
|
32478f1a10 | ||
|
|
abc722b9c0 | ||
|
|
49ebf4a314 | ||
|
|
c503729a9a | ||
|
|
0b7864b09c | ||
|
|
ede16eec3c | ||
|
|
21c1ca2336 | ||
|
|
a305b1ea2d | ||
|
|
17d58d9cc5 | ||
|
|
c605984db0 | ||
|
|
d678a0ebff | ||
|
|
e0fe383815 | ||
|
|
7140efc561 | ||
|
|
99be54fd96 | ||
|
|
35da7906cc | ||
|
|
680ad676ca | ||
|
|
4628c15813 | ||
|
|
8a9960f830 | ||
|
|
2b45433255 | ||
|
|
715da63581 | ||
|
|
ad32bae62f | ||
|
|
bbda0dc3b4 | ||
|
|
dc805dd9b1 | ||
|
|
167cd4e4a0 | ||
|
|
8d68bb7a57 | ||
|
|
7d64ab3158 | ||
|
|
a41d603e0f | ||
|
|
c18220069d | ||
|
|
fbf516284c | ||
|
|
3db25dca7a | ||
|
|
47c7061af5 | ||
|
|
3799ab87ed | ||
|
|
6232a1f941 | ||
|
|
f5ce580593 | ||
|
|
3afa5f7cda | ||
|
|
17b271918f | ||
|
|
3922569d7f | ||
|
|
3a302fe2d7 | ||
|
|
249f86bb21 | ||
|
|
0951a756cc | ||
|
|
fc644925ea | ||
|
|
59c0b63ad0 | ||
|
|
c0f8b3773c | ||
|
|
fd210c6439 | ||
|
|
f7e23cf7c8 | ||
|
|
387351d7ed | ||
|
|
6438d30afc | ||
|
|
b4e1d37b16 | ||
|
|
8ac57d0121 | ||
|
|
d3b51715dc | ||
|
|
b215924b1a | ||
|
|
25d3d66880 | ||
|
|
189574377a | ||
|
|
341ddec9d8 | ||
|
|
abcce78944 | ||
|
|
22e13cd4d2 | ||
|
|
5c105b52b6 | ||
|
|
f757da1a98 | ||
|
|
c1f8db37d9 | ||
|
|
cfc215a013 | ||
|
|
4215a3257b | ||
|
|
1f247ff541 | ||
|
|
2fc46746e2 | ||
|
|
e185dc68af | ||
|
|
54000ff69f | ||
|
|
f6a38e848a | ||
|
|
7e8d670f81 | ||
|
|
287b150b7f | ||
|
|
b379656d55 | ||
|
|
2e11a983c8 | ||
|
|
a9753eb646 | ||
|
|
707c4db881 | ||
|
|
d1de34394e | ||
|
|
e5719441bc | ||
|
|
7153013fb0 | ||
|
|
55500f77fb | ||
|
|
2b826c3adc | ||
|
|
cd193ce8bb | ||
|
|
cb50142ba3 | ||
|
|
81aaed92ce | ||
|
|
4bc551db82 | ||
|
|
471d665408 | ||
|
|
068308ef56 | ||
|
|
1e65c7bf9a | ||
|
|
ae567c08db | ||
|
|
0d25a4868a | ||
|
|
13586be6b0 | ||
|
|
44c649c3c8 | ||
|
|
1a7bfb75fa | ||
|
|
2b803a6a6c | ||
|
|
92e4bdc02e | ||
|
|
4d3d19ca2b | ||
|
|
9c06912efd | ||
|
|
c5893b4445 | ||
|
|
b90933bb8b | ||
|
|
fe9a90854d | ||
|
|
3b012f2827 | ||
|
|
e31dadc99a | ||
|
|
5519e2d4ae | ||
|
|
6285a8b5b2 | ||
|
|
a3139c6fc6 | ||
|
|
389ba9059f | ||
|
|
c0e50be03e | ||
|
|
447833c996 | ||
|
|
809e310565 | ||
|
|
38dd03b5ad | ||
|
|
68f6385eba | ||
|
|
bd376a4992 | ||
|
|
97f65ceac0 | ||
|
|
9e39abcc32 | ||
|
|
5cd2857d5d | ||
|
|
585fcfb7d4 | ||
|
|
d9135a8aac | ||
|
|
081a64223a | ||
|
|
a4eeff01f0 | ||
|
|
eedbe468ac | ||
|
|
463d9513e2 | ||
|
|
ed4aa7dec2 | ||
|
|
d6bbe15a76 | ||
|
|
ea0a0a4a69 | ||
|
|
34442362ca | ||
|
|
b846bb20c6 | ||
|
|
ab3f5f4f4d | ||
|
|
ea63ced2bd | ||
|
|
6bd49bfb72 | ||
|
|
b80d3ce50d | ||
|
|
c069829b33 | ||
|
|
665a113ed8 | ||
|
|
aa7091d962 | ||
|
|
ceff6a9617 | ||
|
|
c01850fc73 | ||
|
|
4c04dc7b84 | ||
|
|
c0103cd878 | ||
|
|
1f1d9d5461 | ||
|
|
c776fa4f7b | ||
|
|
dc91d10395 | ||
|
|
4df10ad26c | ||
|
|
668a88bc86 | ||
|
|
d6e0012818 | ||
|
|
1d5fb52bfc | ||
|
|
8efb02a0ee | ||
|
|
dea42db18b | ||
|
|
78d5d3947f | ||
|
|
a1f5e11517 | ||
|
|
99ad096a8a | ||
|
|
65b4ffeed9 | ||
|
|
0dac88816a | ||
|
|
f7626404b7 | ||
|
|
78f4b08398 | ||
|
|
160fd1c86a | ||
|
|
6ce01487c5 | ||
|
|
b46cbac911 | ||
|
|
e9c3d6bfb7 | ||
|
|
9e9a5b7a53 | ||
|
|
e7fe91c9d4 | ||
|
|
6d130326aa | ||
|
|
02db0f9f9d | ||
|
|
e0668b7507 | ||
|
|
1376f246dc | ||
|
|
113c55d905 | ||
|
|
9cc25bcfd0 | ||
|
|
81d3f78263 | ||
|
|
66596b0b09 | ||
|
|
7455b1019a | ||
|
|
37df934e97 | ||
|
|
6517a95d45 | ||
|
|
4b84a0c916 | ||
|
|
0f0a61e477 | ||
|
|
f64382aa00 | ||
|
|
1743417ec9 | ||
|
|
c61bed52c8 | ||
|
|
edf75865dc | ||
|
|
176a26e6c3 | ||
|
|
10a4d7e849 | ||
|
|
cbe008d52f | ||
|
|
aeb5152789 | ||
|
|
938490df16 | ||
|
|
45c2af80a3 | ||
|
|
892c1b04fd | ||
|
|
1fbf3753bc | ||
|
|
5ead5a94e3 | ||
|
|
bcf435f625 | ||
|
|
8638c46b1d | ||
|
|
b107280b7b | ||
|
|
1a60c20117 | ||
|
|
f1a6926ad9 | ||
|
|
c27a7f09bd | ||
|
|
ba7b9d8168 | ||
|
|
5b070ee32f | ||
|
|
59a126c47c | ||
|
|
322e62418e | ||
|
|
a98d94ccdc | ||
|
|
5addcb517f | ||
|
|
56cbc005ae | ||
|
|
22e9246031 | ||
|
|
c0b39701cc | ||
|
|
e2bac62e36 | ||
|
|
c12a23b84a | ||
|
|
fa95f6d836 | ||
|
|
280e8c7ed1 | ||
|
|
7617fda978 | ||
|
|
af69f7636b | ||
|
|
6d4574130f | ||
|
|
485b6397d0 | ||
|
|
93990327de | ||
|
|
4ee7765403 | ||
|
|
bdbad067b4 | ||
|
|
13a716310c | ||
|
|
d0c77c228b | ||
|
|
36cbffa183 | ||
|
|
5edf9e143f | ||
|
|
b6a1e0d12f | ||
|
|
2fda3a2d26 | ||
|
|
f56eb16941 | ||
|
|
c15c082fb4 | ||
|
|
64e7ab3a12 | ||
|
|
afbd6c811b | ||
|
|
f64c02ce12 | ||
|
|
a0d8aa77d3 | ||
|
|
bed7b29417 | ||
|
|
40ed86bfe0 | ||
|
|
33497c9811 | ||
|
|
52a8597813 | ||
|
|
eeb07f73e5 | ||
|
|
57422c9135 | ||
|
|
adca7cb0c5 | ||
|
|
059f8f5bc9 | ||
|
|
c676e9d794 | ||
|
|
e9f10dd74e | ||
|
|
e29d878d4f | ||
|
|
d4e3ea1412 | ||
|
|
c5462c5f1f | ||
|
|
d1328c3ba9 | ||
|
|
8c406e8e55 | ||
|
|
6e33f36595 | ||
|
|
48277606de | ||
|
|
d7c9fcc8df | ||
|
|
508576544b | ||
|
|
9f2fc21649 | ||
|
|
11f99a963a | ||
|
|
27ab0271a1 | ||
|
|
f858b2858d | ||
|
|
e2809f7bd0 | ||
|
|
f468b37f36 | ||
|
|
0b3d3de30f | ||
|
|
907b909223 | ||
|
|
9dc79f7165 | ||
|
|
324d44dbac | ||
|
|
7a0e695ea0 | ||
|
|
a69a939034 | ||
|
|
ed8efbe759 | ||
|
|
834c6ad8f9 | ||
|
|
2ce48fbc7e | ||
|
|
5d18937e94 | ||
|
|
b3186ba5ea | ||
|
|
f51dc9a1c4 | ||
|
|
f3f9920bd3 | ||
|
|
ea6140e786 | ||
|
|
05c4d6dead | ||
|
|
e2c6f36c70 | ||
|
|
21b1ecb6b3 | ||
|
|
a62cf358ee | ||
|
|
20c429b600 | ||
|
|
38e25a388c | ||
|
|
2c6cedd62c | ||
|
|
9b0cca4a37 | ||
|
|
f6d198a39c | ||
|
|
d12fc6b13c | ||
|
|
a12c7c83d4 | ||
|
|
affd4035c3 | ||
|
|
ecfe1a5442 | ||
|
|
e9c77198d7 | ||
|
|
20be670648 | ||
|
|
a03207e5b4 | ||
|
|
6555b3a49d | ||
|
|
61a634bc1a | ||
|
|
00696a3668 | ||
|
|
e20271791b | ||
|
|
86b9fdbffe | ||
|
|
7e728094a1 | ||
|
|
f865c621ef | ||
|
|
e2f4685a55 | ||
|
|
2148ea94bb | ||
|
|
1d2787250b | ||
|
|
7f02ff12cf | ||
|
|
65341a5d8a | ||
|
|
1608edbb28 | ||
|
|
776da1dea4 | ||
|
|
a6b3e4bbb1 | ||
|
|
3b2ecda243 | ||
|
|
57cbb5c5ce | ||
|
|
7b27d32121 | ||
|
|
ccfba324ee | ||
|
|
9f55a76fcf | ||
|
|
8fdddc310f | ||
|
|
064a4ebe33 | ||
|
|
4981077cb1 | ||
|
|
fbea1c0823 | ||
|
|
a84da88114 | ||
|
|
282b3b5b0a | ||
|
|
130a99c46f | ||
|
|
df4cb7d351 | ||
|
|
1dcff8d463 | ||
|
|
ae4ba6176d | ||
|
|
e461c25428 | ||
|
|
ea4bfdc51d | ||
|
|
554ea8bb95 | ||
|
|
f2be409914 | ||
|
|
3fc1bbea73 | ||
|
|
a97f7d2277 | ||
|
|
af70cdaeac | ||
|
|
8919c3b52a | ||
|
|
f580e20bc3 | ||
|
|
a054cec7c9 | ||
|
|
1ad7bbdd0c | ||
|
|
cfe6759825 | ||
|
|
10c13baf2b | ||
|
|
9fe4e11874 | ||
|
|
f6d8642799 | ||
|
|
adddc5324b | ||
|
|
f442b70ae7 | ||
|
|
7b10213b3a | ||
|
|
ddc79b9070 | ||
|
|
5e9b04b0f5 | ||
|
|
2562eb2aeb | ||
|
|
85da30894b | ||
|
|
bf344e9322 | ||
|
|
f66e222f3d | ||
|
|
fb180d74a1 | ||
|
|
eaf55f5e79 | ||
|
|
8edb586576 | ||
|
|
32b01b8f38 | ||
|
|
340386f282 | ||
|
|
6dd4282f1f | ||
|
|
3f44987799 | ||
|
|
b4fec068d0 | ||
|
|
512632ce60 | ||
|
|
58a9b0d3e8 | ||
|
|
fcb1283a14 | ||
|
|
54671af7f0 | ||
|
|
aedabb0920 | ||
|
|
aaf4acef83 | ||
|
|
8e73cacf4e | ||
|
|
b4b2daebbd | ||
|
|
3e2c18cb4d | ||
|
|
c721fdd793 | ||
|
|
d119372ff0 | ||
|
|
c8bed867da | ||
|
|
3a470ce789 | ||
|
|
f9105719a8 | ||
|
|
d303cbd7cb | ||
|
|
96c09e7264 | ||
|
|
ef19bc7c16 | ||
|
|
aa8472971d | ||
|
|
78257d5ca6 | ||
|
|
655484b1da | ||
|
|
ac8185339d | ||
|
|
a021ce011b | ||
|
|
3687cbfdb3 | ||
|
|
594135fd70 | ||
|
|
2542b9cf59 | ||
|
|
8a7abba427 | ||
|
|
3775649c8a | ||
|
|
9df648b428 | ||
|
|
11a1efdbbc | ||
|
|
a65c2f305e | ||
|
|
50777f5c1d | ||
|
|
ff38cdd09b | ||
|
|
9420913d25 | ||
|
|
59225701b5 | ||
|
|
6aa5d4d58c | ||
|
|
cd2e137fcc | ||
|
|
0b968e1d6b | ||
|
|
7a27fda083 | ||
|
|
aab635154a | ||
|
|
e389f67629 | ||
|
|
169dc779fd | ||
|
|
26423c5c02 | ||
|
|
862543428b | ||
|
|
d28e882f5d | ||
|
|
e26d038589 | ||
|
|
afc8133acf | ||
|
|
abe19a6311 | ||
|
|
07359c76ad | ||
|
|
a2453be573 | ||
|
|
c2616412c0 | ||
|
|
23ca2d9a0b | ||
|
|
0bb186ad3b | ||
|
|
b7abd8328a | ||
|
|
ad2821b4ab | ||
|
|
7d5b08446a | ||
|
|
ba793355cb | ||
|
|
655ca78633 | ||
|
|
f00dcb57cf | ||
|
|
1e52fb546c | ||
|
|
24cad588bd | ||
|
|
bbffde47f7 | ||
|
|
be7598b279 | ||
|
|
1a7e3fbb4b | ||
|
|
e8a0c44e93 | ||
|
|
fe644c76c5 | ||
|
|
9301c56a77 | ||
|
|
e37503c734 | ||
|
|
6bc3f194ec | ||
|
|
878bde67ac | ||
|
|
314d6fa4c5 | ||
|
|
43ff66e4d9 | ||
|
|
2ea91266c0 | ||
|
|
a0bd9bce39 | ||
|
|
5223ec1dbb | ||
|
|
4eda2a2f96 | ||
|
|
fe1975067a | ||
|
|
b9e79c27a8 | ||
|
|
b8ed6a53b6 | ||
|
|
efd71f8bfe | ||
|
|
05a8ba9a8e | ||
|
|
9ce2d1f560 | ||
|
|
5a1225a8bf | ||
|
|
c6069b905b | ||
|
|
2e76620cf8 | ||
|
|
a4b30279ee | ||
|
|
db59d4b2c4 | ||
|
|
faf3802971 | ||
|
|
7fe2a1f802 | ||
|
|
3d57d1bd1d | ||
|
|
ba8bcce8eb | ||
|
|
8247a73182 | ||
|
|
aab409dec2 | ||
|
|
70a9b7bf05 | ||
|
|
d9824a0454 | ||
|
|
e759a249bd | ||
|
|
42c2a66946 | ||
|
|
4d32f2b337 | ||
|
|
0e29744ec2 | ||
|
|
51236a2ad9 | ||
|
|
9b6726a630 | ||
|
|
cbdd05144a | ||
|
|
46d87849f4 | ||
|
|
507f1f196c | ||
|
|
b60febeea2 | ||
|
|
b3e0d8f675 | ||
|
|
9b84a0d516 | ||
|
|
adac5ac544 | ||
|
|
1775995f26 | ||
|
|
df4700b411 | ||
|
|
26a7701cda | ||
|
|
a34085f1d9 | ||
|
|
8e682c715e | ||
|
|
915c19ebda | ||
|
|
7fded367c4 | ||
|
|
0a4743210c | ||
|
|
af19e5d976 | ||
|
|
3d7277398c | ||
|
|
a7ad48a02a | ||
|
|
469a3fc608 | ||
|
|
0fb4ff77f4 | ||
|
|
ac83dba2bb | ||
|
|
979ecf961d | ||
|
|
13dcdf41b8 | ||
|
|
fc96fa756e | ||
|
|
ea9a502152 | ||
|
|
d844734b6c | ||
|
|
ec8a3d2e56 | ||
|
|
5410dc4255 | ||
|
|
d1112bbc99 | ||
|
|
ecf041fa10 | ||
|
|
0ab9bc1db8 | ||
|
|
73e788b94b | ||
|
|
e91a537552 | ||
|
|
ef8c1abf28 | ||
|
|
bd0498aa69 | ||
|
|
ea45033588 | ||
|
|
5e1df7049c | ||
|
|
e27e1a78c3 | ||
|
|
d585a34a26 | ||
|
|
4edaba648e | ||
|
|
9dca7396f3 | ||
|
|
4324242475 | ||
|
|
eca5a05335 | ||
|
|
aa4d3c3ffb | ||
|
|
d442feb687 | ||
|
|
0a88141b18 | ||
|
|
d5e6d82ca1 | ||
|
|
f2a62857cb | ||
|
|
b354ca817d | ||
|
|
4b9bfc178d | ||
|
|
abc2f7e789 | ||
|
|
29cc9a0815 | ||
|
|
f2ee7dcabb | ||
|
|
26203801f6 | ||
|
|
fbe9539130 | ||
|
|
43ec959385 | ||
|
|
9b6276f281 | ||
|
|
0715791229 | ||
|
|
0a0661bf41 | ||
|
|
6ee939d29b | ||
|
|
c3afbc0e53 | ||
|
|
38326314ca | ||
|
|
865950e766 | ||
|
|
d49b67d033 | ||
|
|
6b63808e34 | ||
|
|
34dfcb5add | ||
|
|
30019a144a | ||
|
|
3e222b674a | ||
|
|
e316444c63 | ||
|
|
b29d7beb3a | ||
|
|
f0a49fefd7 | ||
|
|
998c4a5fe5 | ||
|
|
626a6408d0 | ||
|
|
126a5671fe | ||
|
|
aff104fa5d | ||
|
|
a5764351f7 | ||
|
|
348becbbec | ||
|
|
922d6937ae | ||
|
|
c53dae4b72 | ||
|
|
17ad7f7800 | ||
|
|
6232a077b5 | ||
|
|
e7d72beb88 | ||
|
|
01e3f4a4db | ||
|
|
1b76880b0e | ||
|
|
ed4ea7f1f4 | ||
|
|
81e358a01d | ||
|
|
6c283de60a | ||
|
|
4e7a6c0ccf | ||
|
|
eba145503b | ||
|
|
ae8c9d6afc | ||
|
|
faeca4139d | ||
|
|
47909b93f7 | ||
|
|
472658b2fe | ||
|
|
42a03a0436 | ||
|
|
ae0573b3da | ||
|
|
34f816097e | ||
|
|
c651c9f1ed | ||
|
|
7c390cee2c | ||
|
|
987536930c | ||
|
|
10f322198f | ||
|
|
761371509d | ||
|
|
3518ea7e7d | ||
|
|
c92eed2b3e | ||
|
|
0054ce3071 | ||
|
|
b0f74466bb | ||
|
|
b4a0484295 | ||
|
|
bb874012d9 | ||
|
|
bb8583eb14 | ||
|
|
8d2c229bc3 | ||
|
|
48e6208214 | ||
|
|
22233e3ba6 | ||
|
|
fd515654ff | ||
|
|
e439f15a64 | ||
|
|
42175782a5 | ||
|
|
c7a21e0e4d | ||
|
|
d98ffd94f9 | ||
|
|
6ad5da44f3 | ||
|
|
479f422e68 | ||
|
|
e10cdd57a5 | ||
|
|
bf157773c8 | ||
|
|
fba3949530 | ||
|
|
abc3dea8ac | ||
|
|
51d74ac06d | ||
|
|
af835d6efc | ||
|
|
5347b19910 | ||
|
|
29f1cf2b48 | ||
|
|
f56862c684 | ||
|
|
1052be670d | ||
|
|
30a9704625 | ||
|
|
9a9b6ae228 | ||
|
|
7c8dc9fe2d | ||
|
|
b81dd18576 | ||
|
|
dac4b58892 | ||
|
|
71fd430f8e | ||
|
|
5265821bcc | ||
|
|
cb494a74ca | ||
|
|
c526ffbf68 | ||
|
|
6f5fe83a91 | ||
|
|
5a3816c907 | ||
|
|
c8796cf045 | ||
|
|
f4095c6dd0 | ||
|
|
ee2c67a65f | ||
|
|
5614578710 | ||
|
|
08ef78356d | ||
|
|
47ac59abef | ||
|
|
265a896211 | ||
|
|
f7e4fca70d | ||
|
|
bb4c443cd9 | ||
|
|
7b1d2ee050 | ||
|
|
b3f70a046f | ||
|
|
734e87f85f | ||
|
|
f371c5fd62 | ||
|
|
4b3edbd2f5 | ||
|
|
1f3106b9da | ||
|
|
9fd3a9a82d | ||
|
|
a6e6991a2d | ||
|
|
93ba0717d8 | ||
|
|
a8839e0ef4 | ||
|
|
3e4152c966 | ||
|
|
1bf34d73f5 | ||
|
|
d1e360d64d | ||
|
|
3b5b19848c | ||
|
|
93ba90e837 | ||
|
|
fc70d79a17 | ||
|
|
65016a2383 | ||
|
|
dbcb2ccb46 | ||
|
|
2afcfcc87c | ||
|
|
1afef9416a | ||
|
|
4049143ebf | ||
|
|
4df53bdf8d | ||
|
|
d49a1ea304 | ||
|
|
521f4facd5 | ||
|
|
b4653dfc15 |
1003
.all-contributorsrc
1003
.all-contributorsrc
File diff suppressed because it is too large
Load Diff
40
.env.example
40
.env.example
@@ -7,29 +7,31 @@ APP_KEY=ChangeMe
|
||||
APP_URL=null
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
|
||||
MAX_RESULTS=500
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DATABASE SETTINGS
|
||||
# --------------------------------------------
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
DB_HOST=127.0.0.1
|
||||
DB_DATABASE=null
|
||||
DB_USERNAME=null
|
||||
DB_PASSWORD=null
|
||||
DB_PREFIX=null
|
||||
DB_DUMP_PATH='/usr/bin'
|
||||
DB_CHARSET=utf8mb4
|
||||
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
|
||||
DB_SSL_CIPHER=null
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
@@ -43,7 +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
|
||||
@@ -51,7 +53,6 @@ MAIL_REPLYTO_NAME='Snipe-IT'
|
||||
# --------------------------------------------
|
||||
IMAGE_LIB=gd
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SESSION SETTINGS
|
||||
# --------------------------------------------
|
||||
@@ -62,6 +63,15 @@ COOKIE_NAME=snipeit_session
|
||||
COOKIE_DOMAIN=null
|
||||
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
|
||||
ENABLE_HSTS=false
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: CACHE SETTINGS
|
||||
@@ -69,7 +79,20 @@ SECURE_COOKIES=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
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MEMCACHED SETTINGS
|
||||
# --------------------------------------------
|
||||
MEMCACHED_HOST=null
|
||||
MEMCACHED_PORT=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: AWS S3 SETTINGS
|
||||
@@ -89,8 +112,11 @@ LOGIN_LOCKOUT_DURATION=60
|
||||
# OPTIONAL: MISC
|
||||
# --------------------------------------------
|
||||
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
|
||||
15
.github/ISSUE_TEMPLATE.md
vendored
15
.github/ISSUE_TEMPLATE.md
vendored
@@ -12,20 +12,27 @@
|
||||
|
||||
#### Please confirm you have done the following before posting your bug report:
|
||||
|
||||
- [ ] I have enabled debug mode
|
||||
- [ ] I have enabled debug mode
|
||||
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
|
||||
|
||||
-----
|
||||
#### Please provide answers to these questions before posting your bug report:
|
||||
#### Provide answers to these questions:
|
||||
|
||||
- Is this a fresh install or an upgrade?
|
||||
- Version of Snipe-IT you're running
|
||||
- Version of PHP you're running
|
||||
- Version of MySQL/MariaDB you're running
|
||||
- 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)
|
||||
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
|
||||
- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the 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 [reproduceable on the demo](https://snipeitapp.com/demo).
|
||||
- Include any additional information you can find in `app/storage/logs` and your webserver's logs.
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
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.
|
||||
|
||||
https://snipe-it.readme.io/docs/getting-help
|
||||
|
||||
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.
|
||||
43
.github/stale.yml
vendored
Normal file
43
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- :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. 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: >
|
||||
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
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -27,8 +27,16 @@ public/uploads/logo.png
|
||||
public/uploads/logo.svg
|
||||
public/uploads/models/*
|
||||
public/uploads/suppliers/*
|
||||
public/uploads/accessories/*
|
||||
public/uploads/locations/*
|
||||
public/uploads/manufacturers/*
|
||||
public/uploads/components/*
|
||||
public/uploads/consumables/*
|
||||
public/uploads/companies/*
|
||||
public/uploads/categories/*
|
||||
public/uploads/users/*
|
||||
storage/app/private_uploads/users/*
|
||||
public/uploads/departments/*
|
||||
storage/debugbar/
|
||||
storage/dumps/*
|
||||
storage/laravel-backups
|
||||
@@ -42,3 +50,4 @@ tests/_support/_generated/*
|
||||
/storage/oauth-public.key
|
||||
|
||||
*.cache
|
||||
/public/storage
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -1,4 +1,7 @@
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token:
|
||||
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
|
||||
hosts:
|
||||
- localhost
|
||||
sudo: false
|
||||
@@ -11,11 +14,17 @@ services:
|
||||
|
||||
# list any PHP version you want to test against
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1.2
|
||||
- 7.2
|
||||
- 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;'
|
||||
@@ -43,11 +52,14 @@ before_script:
|
||||
# use the $DB env variable to determine the phpunit.xml to use
|
||||
# script: ./vendor/bin/codecept run --env testing-ci
|
||||
script:
|
||||
- ./vendor/bin/codecept run unit --env testing-ci
|
||||
- ./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
|
||||
|
||||
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
||||
reported by contacting the project team at abuse@snipeitapp.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
### Contributing
|
||||
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing).
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
59
README.md
59
README.md
@@ -1,16 +1,16 @@
|
||||
[](https://travis-ci.org/snipe/snipe-it) [](http://waffle.io/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://zenhub.io) [](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://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.
|
||||
|
||||
-----
|
||||
|
||||
@@ -50,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:
|
||||
@@ -67,7 +91,22 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/8341172?v=3" width="110px;"/><br /><sub>Jay Richards</sub>](http://www.cordeos.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=technogenus "Code") | [<img src="https://avatars2.githubusercontent.com/u/7295127?v=3" width="110px;"/><br /><sub>Alexander Innes</sub>](https://necurity.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leostat "Code") | [<img src="https://avatars2.githubusercontent.com/u/334485?v=3" width="110px;"/><br /><sub>Danny Garcia</sub>](https://buzzedword.codes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=buzzedword "Code") | [<img src="https://avatars2.githubusercontent.com/u/366855?v=3" width="110px;"/><br /><sub>archpoint</sub>](https://github.com/archpoint)<br />[💻](https://github.com/snipe/snipe-it/commits?author=archpoint "Code") | [<img src="https://avatars1.githubusercontent.com/u/67991?v=3" width="110px;"/><br /><sub>Jake McGraw</sub>](http://www.jakemcgraw.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jakemcgraw "Code") | [<img src="https://avatars1.githubusercontent.com/u/1714374?v=3" width="110px;"/><br /><sub>FleischKarussel</sub>](https://github.com/FleischKarussel)<br />[📖](https://github.com/snipe/snipe-it/commits?author=FleischKarussel "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/319644?v=3" width="110px;"/><br /><sub>Dylan Yi</sub>](https://github.com/feeva)<br />[💻](https://github.com/snipe/snipe-it/commits?author=feeva "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/857740?v=3" width="110px;"/><br /><sub>Gil Rutkowski</sub>](http://FlashingCursor.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [<img src="https://avatars3.githubusercontent.com/u/129360?v=3" width="110px;"/><br /><sub>Desmond Morris</sub>](http://www.desmondmorris.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [<img src="https://avatars2.githubusercontent.com/u/52936?v=3" width="110px;"/><br /><sub>Nick Peelman</sub>](http://peelman.us)<br />[💻](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [<img src="https://avatars0.githubusercontent.com/u/53161?v=3" width="110px;"/><br /><sub>Abraham Vegh</sub>](https://abrahamvegh.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [<img src="https://avatars0.githubusercontent.com/u/2818680?v=3" width="110px;"/><br /><sub>Mohamed Rashid</sub>](https://github.com/rashivkp)<br />[📖](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1509456?v=3" width="110px;"/><br /><sub>Kasey</sub>](http://hinchk.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [<img src="https://avatars2.githubusercontent.com/u/10522541?v=3" width="110px;"/><br /><sub>Brett</sub>](https://github.com/BrettFagerlund)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") |
|
||||
| [<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://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") [🌍](#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!
|
||||
@@ -76,13 +115,7 @@ This project follows the [all-contributors](https://github.com/kentcdodds/all-co
|
||||
|
||||
### Contributing
|
||||
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing).
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
|
||||
|
||||
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
|
||||
112
app/Console/Commands/CheckoutLicenseToAllUsers.php
Normal file
112
app/Console/Commands/CheckoutLicenseToAllUsers.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\License;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CheckoutLicenseToAllUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:checkout-to-all {--license_id=} {--notify}';
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
|
||||
$license_id = $this->option('license_id');
|
||||
$notify = $this->option('notify');
|
||||
|
||||
if (!$license_id) {
|
||||
$this->error('ERROR: License ID is required.');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!$license = License::where('id','=',$license_id)->with('assignedusers')->first()) {
|
||||
$this->error('Invalid license ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
$users = User::whereNull('deleted_at')->with('licenses')->get();
|
||||
|
||||
if ($users->count() > $license->getAvailSeatsCountAttribute()) {
|
||||
$this->info('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
|
||||
}
|
||||
|
||||
$this->info('Checking out '.$users->count().' of '.$license->getAvailSeatsCountAttribute().' seats for '.$license->name);
|
||||
|
||||
if (!$notify) {
|
||||
$this->info('No mail will be sent.');
|
||||
}
|
||||
|
||||
foreach ($users as $user) {
|
||||
|
||||
// Check to make sure this user doesn't already have this license checked out
|
||||
// to them
|
||||
|
||||
if ($user->licenses->where('id', '=', $license_id)->count()) {
|
||||
$this->info($user->username .' already has this license checked out to them. Skipping... ');
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// If the license is valid, check that there is an available seat
|
||||
if ($license->availCount()->count() < 1) {
|
||||
$this->error('ERROR: No available seats');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->info($license->availCount()->count().' seats left');
|
||||
// Get the seat ID
|
||||
$licenseSeat = $license->freeSeat();
|
||||
|
||||
|
||||
// Update the seat with checkout info,
|
||||
$licenseSeat->assigned_to = $user->id;
|
||||
if ($licenseSeat->save()) {
|
||||
|
||||
// Temporarily null the user's email address so we don't send mail if we're not supposed to
|
||||
if (!$notify) {
|
||||
$user->email = null;
|
||||
}
|
||||
|
||||
// Log the checkout
|
||||
$licenseSeat->logCheckout('Checked out via cli tool', $user);
|
||||
$this->info('License '.$license_id.' seat '.$licenseSeat->id.' checked out to '.$user->username);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
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() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class LdapSync extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--summary}';
|
||||
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -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;
|
||||
@@ -55,31 +54,37 @@ class LdapSync extends Command
|
||||
|
||||
try {
|
||||
$ldapconn = Ldap::connectToLdap();
|
||||
} catch (\Exception $e) {
|
||||
LOG::error($e);
|
||||
}
|
||||
|
||||
try {
|
||||
Ldap::bindAdminToLdap($ldapconn);
|
||||
} catch (\Exception $e) {
|
||||
LOG::error($e);
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
$summary = array();
|
||||
|
||||
$results = Ldap::findLdapUsers();
|
||||
|
||||
$ldap_ou_locations = Location::whereNotNull('ldap_ou')->get();
|
||||
|
||||
if (sizeof($ldap_ou_locations) > 0) {
|
||||
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
|
||||
try {
|
||||
if ($this->option('base_dn') != '') {
|
||||
$search_base = $this->option('base_dn');
|
||||
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
} else {
|
||||
$search_base = null;
|
||||
}
|
||||
$results = Ldap::findLdapUsers($search_base);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
$results = Ldap::findLdapUsers();
|
||||
for ($i = 0; $i < $results["count"]; $i++) {
|
||||
$results[$i]["ldap_location_override"] = false;
|
||||
$results[$i]["location_id"] = 0;
|
||||
}
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = NULL;
|
||||
|
||||
if ($this->option('location')!='') {
|
||||
$location = Location::where('name', '=', $this->option('location'))->first();
|
||||
@@ -89,40 +94,67 @@ class LdapSync extends Command
|
||||
$location = Location::where('id', '=', $this->option('location_id'))->first();
|
||||
LOG::debug('Location ID '.$this->option('location_id').' passed');
|
||||
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
} else {
|
||||
$location = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($location)) {
|
||||
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
// Grab subsets based on location-specific DNs, and overwrite location for these users.
|
||||
foreach ($ldap_ou_locations as $ldap_loc) {
|
||||
$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];
|
||||
/* Process locations with explicitly defined OUs, if doing a full import. */
|
||||
if ($this->option('base_dn')=='') {
|
||||
// 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"]);
|
||||
}
|
||||
|
||||
// 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]);
|
||||
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
|
||||
|
||||
if (sizeof($ldap_ou_locations) > 0) {
|
||||
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
|
||||
}
|
||||
|
||||
// Inject location information fields
|
||||
for ($i = 0; $i < $results["count"]; $i++) {
|
||||
$results[$i]["ldap_location_override"] = false;
|
||||
$results[$i]["location_id"] = 0;
|
||||
}
|
||||
|
||||
// Grab subsets based on location-specific DNs, and overwrite location for these users.
|
||||
foreach ($ldap_ou_locations as $ldap_loc) {
|
||||
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
|
||||
$usernames = array();
|
||||
for ($i = 0; $i < $location_users["count"]; $i++) {
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$global_count = $results['count'];
|
||||
$results = array_merge($location_users, $results);
|
||||
$results['count'] = $global_count;
|
||||
// Delete located users from the general group.
|
||||
foreach ($results as $key => $generic_entry) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$global_count = $results['count'];
|
||||
$results = array_merge($location_users, $results);
|
||||
$results['count'] = $global_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create user account entries in Snipe-IT */
|
||||
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
$pass = bcrypt($tmp_pass);
|
||||
|
||||
|
||||
for ($i = 0; $i < $results["count"]; $i++) {
|
||||
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
|
||||
|
||||
@@ -135,32 +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"]:"";
|
||||
|
||||
|
||||
// User exists
|
||||
$item["createorupdate"] = 'updated';
|
||||
if (!$user = User::where('username', $item["username"])->first()) {
|
||||
$user = User::where('username', $item["username"])->first();
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item["createorupdate"] = 'updated';
|
||||
} else {
|
||||
// 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 = 1;
|
||||
|
||||
// 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'];
|
||||
} else if ($location) {
|
||||
$user->location_id = e($location->id);
|
||||
} elseif ((isset($location)) && (!empty($location))) {
|
||||
|
||||
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 = '';
|
||||
@@ -184,17 +233,16 @@ class LdapSync extends Command
|
||||
if ($this->option('summary')) {
|
||||
for ($x = 0; $x < count($summary); $x++) {
|
||||
if ($summary[$x]['status']=='error') {
|
||||
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was not imported: '.$summary[$x]['note']);
|
||||
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
|
||||
} else {
|
||||
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].' was '.strtoupper($summary[$x]['createorupdate']).'.');
|
||||
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
|
||||
}
|
||||
|
||||
}
|
||||
} else if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ];
|
||||
$this->info(json_encode($json_summary));
|
||||
} else {
|
||||
return $summary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
109
app/Console/Commands/MergeUsersByUsername.php
Normal file
109
app/Console/Commands/MergeUsersByUsername.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
|
||||
|
||||
class MergeUsersByUsername extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:merge-users';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command allows you to merge the history of users. It looks for users without an email address as their username and merges them into the version that does have an email username.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Get the list of users who have an email address as their username
|
||||
$users = User::where('username', 'LIKE', '%@%')->whereNull('deleted_at')->get();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$parts = explode("@", $user->username);
|
||||
$bad_users = User::where('username', '=', $parts[0])->whereNull('deleted_at')->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations')->get();
|
||||
|
||||
foreach ($bad_users as $bad_user) {
|
||||
$this->info($bad_user->username.' ('.$bad_user->id.') will be merged into '.$user->username.' ('.$user->id.') ');
|
||||
|
||||
// Walk the list of assets
|
||||
foreach ($bad_user->assets as $asset) {
|
||||
$this->info( 'Updating asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
|
||||
$asset->assigned_to = $user->id;
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
// Walk the list of licenses
|
||||
foreach ($bad_user->licenses as $license) {
|
||||
$this->info( 'Updating license '.$license->name.' '.$license->id.' to user '.$user->id);
|
||||
$bad_user->licenses()->updateExistingPivot($license->id, ['assigned_to' => $user->id]);
|
||||
}
|
||||
|
||||
// Walk the list of consumables
|
||||
foreach ($bad_user->consumables as $consumable) {
|
||||
$this->info( 'Updating consumable '.$consumable->id.' to user '.$user->id);
|
||||
$bad_user->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $user->id]);
|
||||
}
|
||||
|
||||
// Walk the list of accessories
|
||||
foreach ($bad_user->accessories as $accessory) {
|
||||
$this->info( 'Updating accessory '.$accessory->id.' to user '.$user->id);
|
||||
$bad_user->accessories()->updateExistingPivot($accessory->id, ['assigned_to' => $user->id]);
|
||||
}
|
||||
|
||||
// Walk the list of logs
|
||||
foreach ($bad_user->userlog as $log) {
|
||||
$this->info( 'Updating action log record '.$log->id.' to user '.$user->id);
|
||||
$log->target_id = $user->id;
|
||||
$log->save();
|
||||
}
|
||||
|
||||
// Update any manager IDs
|
||||
$this->info( 'Updating managed user records to user '.$user->id);
|
||||
User::where('manager_id', '=', $bad_user->id)->update(['manager_id' => $user->id]);
|
||||
|
||||
|
||||
// Update location manager IDs
|
||||
foreach ($bad_user->managedLocations as $managedLocation) {
|
||||
$this->info( 'Updating managed location record '.$managedLocation->name.' to manager '.$user->id);
|
||||
$managedLocation->manager_id = $user->id;
|
||||
$managedLocation->save();
|
||||
}
|
||||
|
||||
// Mark the user as deleted
|
||||
$this->info( 'Marking the user as deleted');
|
||||
$bad_user->deleted_at = Carbon::now()->timestamp;
|
||||
$bad_user->save();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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,16 +74,12 @@ 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');
|
||||
\Log::useFiles($logFile);
|
||||
if ($this->option('testrun')) {
|
||||
$this->comment('====== TEST ONLY Item Import for '.$filename.' ====');
|
||||
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
|
||||
} else {
|
||||
$this->comment('======= Importing Items from '.$filename.' =========');
|
||||
}
|
||||
$this->comment('======= Importing Items from '.$filename.' =========');
|
||||
$importer->import();
|
||||
|
||||
$this->bar = null;
|
||||
@@ -177,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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ class RecryptFromMcrypt extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:legacy-recrypt';
|
||||
protected $signature = 'snipeit:legacy-recrypt
|
||||
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -47,6 +48,8 @@ class RecryptFromMcrypt extends Command
|
||||
// Check and see if they have a legacy app key listed in their .env
|
||||
// If not, we can try to use the current APP_KEY if looks like it's old
|
||||
$legacy_key = env('LEGACY_APP_KEY');
|
||||
$key_parts = explode(':', $legacy_key);
|
||||
$legacy_cipher = env('LEGACY_CIPHER', 'rijndael-256');
|
||||
$errors = array();
|
||||
|
||||
if (!$legacy_key) {
|
||||
@@ -54,11 +57,24 @@ class RecryptFromMcrypt extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the app key is 32 characters
|
||||
|
||||
// Do some basic legacy app key length checks
|
||||
if (strlen($legacy_key) == 32) {
|
||||
$this->comment('INFO: Your LEGACY_APP_KEY is 32 characters. Okay to continue.');
|
||||
$legacy_length_check = true;
|
||||
} elseif (array_key_exists('1', $key_parts) && (strlen($key_parts[1])==44)) {
|
||||
$legacy_key = base64_decode($key_parts[1],true);
|
||||
$legacy_length_check = true;
|
||||
} else {
|
||||
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
|
||||
$legacy_length_check = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check that the app key is 32 characters
|
||||
if ($legacy_length_check === true) {
|
||||
$this->comment('INFO: Your LEGACY_APP_KEY looks correct. Okay to continue.');
|
||||
} else {
|
||||
$this->error('ERROR: Your LEGACY_APP_KEY is not the correct length (32 characters or base64 followed by 44 characters for later versions). Please locate your old APP_KEY and use that as your LEGACY_APP_KEY in your .env file to continue.');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,7 +82,9 @@ class RecryptFromMcrypt extends Command
|
||||
$this->error('================================!!!! WARNING !!!!================================');
|
||||
$this->comment("This tool will attempt to decrypt your old Snipe-IT (mcrypt, now deprecated) encrypted data and re-encrypt it using OpenSSL. \n\nYou should only continue if you have backed up any and all old APP_KEYs and have backed up your data.");
|
||||
|
||||
if ($this->confirm("Are you SURE you wish to continue?")) {
|
||||
$force = ($this->option('force')) ? true : false;
|
||||
|
||||
if ($force || ($this->confirm("Are you SURE you wish to continue?"))) {
|
||||
|
||||
$backup_file = 'backups/env-backups/'.'app_key-'.date('Y-m-d-gis');
|
||||
|
||||
@@ -78,13 +96,21 @@ class RecryptFromMcrypt extends Command
|
||||
}
|
||||
|
||||
|
||||
$mcrypter = new McryptEncrypter($legacy_key);
|
||||
if ($legacy_cipher){
|
||||
$mcrypter = new McryptEncrypter($legacy_key,$legacy_cipher);
|
||||
}else{
|
||||
$mcrypter = new McryptEncrypter($legacy_key);
|
||||
}
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->ldap_password=='') {
|
||||
if ($settings->ldap_pword=='') {
|
||||
$this->comment('INFO: No LDAP password found. Skipping... ');
|
||||
} else {
|
||||
$decrypted_ldap_pword = $mcrypter->decrypt($settings->ldap_pword);
|
||||
$settings->ldap_pword = \Crypt::encrypt($decrypted_ldap_pword);
|
||||
$settings->save();
|
||||
}
|
||||
|
||||
/** @var CustomField[] $custom_fields */
|
||||
$custom_fields = CustomField::where('field_encrypted','=', 1)->get();
|
||||
$this->comment('INFO: Retrieving encrypted custom fields...');
|
||||
|
||||
@@ -97,32 +123,22 @@ class RecryptFromMcrypt extends Command
|
||||
|
||||
|
||||
// Get all assets with a value in any of the fields that were encrypted
|
||||
/** @var Asset[] $assets */
|
||||
$assets = $query->get();
|
||||
|
||||
$bar = $this->output->createProgressBar(count($assets));
|
||||
|
||||
foreach ($custom_fields as $encrypted_field) {
|
||||
|
||||
// Try to decrypt the payload using the legacy app key
|
||||
try {
|
||||
$decrypted_field = $mcrypter->decrypt($encrypted_field);
|
||||
$this->comment($decrypted_field);
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
foreach ($custom_fields as $encrypted_field) {
|
||||
$columnName = $encrypted_field->db_column;
|
||||
|
||||
// Make sure the value isn't null
|
||||
if ($asset->{$encrypted_field}!='') {
|
||||
if ($asset->{$columnName}!='') {
|
||||
// Try to decrypt the payload using the legacy app key
|
||||
try {
|
||||
$decrypted_field = $mcrypter->decrypt($asset->{$encrypted_field});
|
||||
$asset->{$encrypted_field} = \Crypt::encrypt($decrypted_field);
|
||||
$decrypted_field = $mcrypter->decrypt($asset->{$columnName});
|
||||
$asset->{$columnName} = \Crypt::encrypt($decrypted_field);
|
||||
$this->comment($decrypted_field);
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = ' - ERROR: Could not decrypt field ['.$encrypted_field->name.']: '.$e->getMessage();
|
||||
|
||||
105
app/Console/Commands/RegenerateAssetTags.php
Normal file
105
app/Console/Commands/RegenerateAssetTags.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use DB;
|
||||
use Artisan;
|
||||
|
||||
class RegenerateAssetTags extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:regenerate-tags {--start=} {--output= : info|warn|error|all} ';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This utility will regenerate all asset tags. THIS IS DATA-DESTRUCTIVE AND SHOULD BE USED WITH CAUTION. ';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if ($this->confirm('This will regenerate all of the asset tags within your system. This action is data-destructive and should be used with caution. Do you wish to continue?'))
|
||||
{
|
||||
|
||||
$output['info'] = [];
|
||||
$output['warn'] = [];
|
||||
$output['error'] = [];
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$start_tag = ($this->option('start')) ? $this->option('start') : (($settings->next_auto_tag_base) ? Setting::getSettings()->next_auto_tag_base : 1) ;
|
||||
|
||||
$this->info('Starting at '.$start_tag);
|
||||
|
||||
$total_assets = Asset::orderBy('id','asc')->get();
|
||||
$bar = $this->output->createProgressBar(count($total_assets));
|
||||
|
||||
try {
|
||||
Artisan::call('backup:run');
|
||||
} catch (\Exception $e) {
|
||||
$output['error'][] = $e;
|
||||
}
|
||||
|
||||
foreach ($total_assets as $asset) {
|
||||
|
||||
$start_tag++;
|
||||
$output['info'][] = 'Asset tag:'.$asset->asset_tag;
|
||||
$asset->asset_tag = $settings->auto_increment_prefix.$settings->auto_increment_prefix.$start_tag;
|
||||
|
||||
if ($settings->zerofill_count > 0) {
|
||||
$asset->asset_tag = $settings->auto_increment_prefix.Asset::zerofill($start_tag, $settings->zerofill_count);
|
||||
}
|
||||
|
||||
$output['info'][] = 'New Asset tag:'.$asset->asset_tag;
|
||||
|
||||
// Use forceSave here to override model level validation
|
||||
$asset->forceSave();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->info("\n");
|
||||
|
||||
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='info')) {
|
||||
foreach ($output['info'] as $key => $output_text) {
|
||||
$this->info($output_text);
|
||||
}
|
||||
}
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='warn')) {
|
||||
foreach ($output['warn'] as $key => $output_text) {
|
||||
$this->warn($output_text);
|
||||
}
|
||||
}
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='error')) {
|
||||
foreach ($output['error'] as $key => $output_text) {
|
||||
$this->error($output_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
78
app/Console/Commands/ResetDemoSettings.php
Normal file
78
app/Console/Commands/ResetDemoSettings.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
||||
class ResetDemoSettings extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:demo-settings';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This will reset the Snipe-IT demo settings back to default. ';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$this->info('Resetting the demo settings.');
|
||||
$settings = Setting::first();
|
||||
$settings->per_page = 20;
|
||||
$settings->site_name = 'Snipe-IT Asset Management Demo';
|
||||
$settings->auto_increment_assets = 1;
|
||||
$settings->logo = 'snipe-logo.png';
|
||||
$settings->alert_email = 'service@snipe-it.io';
|
||||
$settings->header_color = null;
|
||||
$settings->barcode_type = 'QRCODE';
|
||||
$settings->default_currency = 'USD';
|
||||
$settings->brand = 3;
|
||||
$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';
|
||||
$settings->date_display_format = 'D M d, Y';
|
||||
$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()) {
|
||||
$user->locale = 'en';
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
73
app/Console/Commands/SendExpectedCheckinAlerts.php
Normal file
73
app/Console/Commands/SendExpectedCheckinAlerts.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'snipeit:expected-checkin';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check for overdue or upcoming expected checkins.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$whenNotify = Carbon::now()->addDays(7);
|
||||
$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->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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
148
app/Console/Commands/SyncAssetLocations.php
Normal file
148
app/Console/Commands/SyncAssetLocations.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SyncAssetLocations extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:sync-asset-locations {--output= : info|warn|error|all} ';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This utility will sync the location_id of assets based on current state. It should not normally be needed, but is a safeguard in case we missed something in the Great Migration when flattening the assets to location relationship.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$output['info'] = [];
|
||||
$output['warn'] = [];
|
||||
$output['error'] = [];
|
||||
|
||||
$total_assets = Asset::whereNull('deleted_at')->get();
|
||||
$bar = $this->output->createProgressBar(count($total_assets));
|
||||
|
||||
// Unassigned
|
||||
$rtd_assets = Asset::whereNull('assigned_to')->whereNull('deleted_at')->with('defaultLoc')->get();
|
||||
$output['info'][] = 'There are '.$rtd_assets->count().' unassigned assets.';
|
||||
|
||||
foreach ($rtd_assets as $rtd_asset) {
|
||||
$output['info'][] = 'Setting Unassigned Asset ' . $rtd_asset->id . ' ('.$rtd_asset->asset_tag.') to location: ' . $rtd_asset->rtd_location_id . " because their default location is: " . $rtd_asset->rtd_location_id;
|
||||
$rtd_asset->location_id=$rtd_asset->rtd_location_id;
|
||||
$rtd_asset->unsetEventDispatcher();
|
||||
$rtd_asset->save();
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$assigned_user_assets = Asset::where('assigned_type','App\Models\User')->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
|
||||
$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;
|
||||
$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! ';
|
||||
$new_location = $assigned_user_asset->rtd_location_id;
|
||||
}
|
||||
$assigned_user_asset->location_id=$new_location;
|
||||
$assigned_user_asset->unsetEventDispatcher();
|
||||
$assigned_user_asset->save();
|
||||
$bar->advance();
|
||||
|
||||
}
|
||||
|
||||
$assigned_location_assets = Asset::where('assigned_type','App\Models\Location')
|
||||
->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
|
||||
$output['info'][] = 'There are '.$assigned_location_assets->count().' assets checked out to locations.';
|
||||
|
||||
foreach ($assigned_location_assets as $assigned_location_asset) {
|
||||
if ($assigned_location_asset->assignedTo) {
|
||||
$assigned_location_asset->location_id = $assigned_location_asset->assignedTo->id;
|
||||
$output['info'][] ='Setting Location Assigned asset ' . $assigned_location_asset->id . ' ('.$assigned_location_asset->asset_tag.') that is checked out to '.$assigned_location_asset->assignedTo->name.' (#'.$assigned_location_asset->assignedTo->id.') to location: ' . $assigned_location_asset->assetLoc()->id;
|
||||
$assigned_location_asset->unsetEventDispatcher();
|
||||
$assigned_location_asset->save();
|
||||
} else {
|
||||
$output['warn'][] ='Asset ' . $assigned_location_asset->id . ' ('.$assigned_location_asset->asset_tag.') did not return a valid associated location - perhaps it was deleted?';
|
||||
}
|
||||
$bar->advance();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Assigned to assets
|
||||
$assigned_asset_assets = Asset::where('assigned_type','App\Models\Asset')
|
||||
->whereNotNull('assigned_to')->whereNull('deleted_at')->get();
|
||||
$output['info'][] ='Asset-assigned assets: '.$assigned_asset_assets->count();
|
||||
|
||||
foreach ($assigned_asset_assets as $assigned_asset_asset) {
|
||||
|
||||
// Check to make sure there aren't any invalid relationships
|
||||
if ($assigned_asset_asset->assetLoc()) {
|
||||
$assigned_asset_asset->location_id = $assigned_asset_asset->assetLoc()->id;
|
||||
$output['info'][] ='Setting Asset Assigned asset ' . $assigned_asset_asset->assetLoc()->id. ' ('.$assigned_asset_asset->asset_tag.') location to: ' . $assigned_asset_asset->assetLoc()->id;
|
||||
$assigned_asset_asset->unsetEventDispatcher();
|
||||
$assigned_asset_asset->save();
|
||||
} else {
|
||||
$output['warn'][] ='Asset Assigned asset ' . $assigned_asset_asset->id. ' ('.$assigned_asset_asset->asset_tag.') does not seem to have a valid location';
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
|
||||
}
|
||||
|
||||
$unlocated_assets = Asset::whereNull("location_id")->whereNull('deleted_at')->get();
|
||||
$output['info'][] ='Assets still without a location: '.$unlocated_assets->count();
|
||||
foreach($unlocated_assets as $unlocated_asset) {
|
||||
$output['warn'][] ='Asset: '.$unlocated_asset->id.' still has no location. ';
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->info("\n");
|
||||
|
||||
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='info')) {
|
||||
foreach ($output['info'] as $key => $output_text) {
|
||||
$this->info($output_text);
|
||||
}
|
||||
}
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='warn')) {
|
||||
foreach ($output['warn'] as $key => $output_text) {
|
||||
$this->warn($output_text);
|
||||
}
|
||||
}
|
||||
if (($this->option('output')=='all') || ($this->option('output')=='error')) {
|
||||
foreach ($output['error'] as $key => $output_text) {
|
||||
$this->error($output_text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class SystemBackup extends Command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
$this->call('backup:run');
|
||||
|
||||
135
app/Console/Commands/Version.php
Normal file
135
app/Console/Commands/Version.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Version extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'version:update {--branch=master} {--type=patch}';
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
|
||||
$use_branch = $this->option('branch');
|
||||
$use_type = $this->option('type');
|
||||
$git_branch = trim(shell_exec('git rev-parse --abbrev-ref HEAD'));
|
||||
$build_version = trim(shell_exec('git rev-list --count '.$use_branch));
|
||||
$versionFile = 'config/version.php';
|
||||
$full_hash_version = str_replace("\n", '', shell_exec('git describe master --tags'));
|
||||
|
||||
$version = explode('-', $full_hash_version);
|
||||
$app_version = $current_app_version = $version[0];
|
||||
$hash_version = (array_key_exists('2', $version)) ? $version[2] : '';
|
||||
$prerelease_version = '';
|
||||
|
||||
$this->line('Branch is: '.$use_branch);
|
||||
$this->line('Type is: '.$use_type);
|
||||
$this->line('Current version is: '.$full_hash_version);
|
||||
|
||||
if (count($version)==3) {
|
||||
$this->line('This does not look like an alpha/beta release.');
|
||||
} else {
|
||||
if (array_key_exists('3',$version)) {
|
||||
$this->line('The current version looks like a beta release.');
|
||||
$prerelease_version = $version[1];
|
||||
$hash_version = $version[3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$app_version_raw = explode('.', $app_version);
|
||||
|
||||
$maj = str_replace('v', '', $app_version_raw[0]);
|
||||
$min = $app_version_raw[1];
|
||||
$patch = '';
|
||||
|
||||
|
||||
|
||||
// This is a major release that might not have a third .0
|
||||
if (array_key_exists(2, $app_version_raw)) {
|
||||
$patch = $app_version_raw[2];
|
||||
}
|
||||
|
||||
if ($use_type=='major') {
|
||||
$app_version = "v".($maj + 1).".$min.$patch";
|
||||
} elseif ($use_type=='minor') {
|
||||
$app_version = "v"."$maj.".($min + 1).".$patch";
|
||||
} elseif ($use_type=='pre') {
|
||||
$pre_raw = str_replace('beta','', $prerelease_version);
|
||||
$pre_raw = str_replace('alpha','', $pre_raw);
|
||||
$pre_raw = str_ireplace('rc','', $pre_raw);
|
||||
$pre_raw = $pre_raw++;
|
||||
$this->line('Setting the pre-release to '. $prerelease_version.'-'.$pre_raw);
|
||||
$app_version = "v"."$maj.".($min + 1).".$patch";
|
||||
} elseif ($use_type=='patch') {
|
||||
$app_version = "v" . "$maj.$min." . ($patch + 1);
|
||||
// If nothing is passed, leave the version as it is, just increment the build
|
||||
} else {
|
||||
$app_version = "v" . "$maj.$min." . $patch;
|
||||
}
|
||||
|
||||
// Determine if this tag already exists, or if this prior to a release
|
||||
$this->line('Running: git rev-parse master '.$current_app_version);
|
||||
// $pre_release = trim(shell_exec('git rev-parse '.$use_branch.' '.$current_app_version.' 2>&1 1> /dev/null'));
|
||||
|
||||
if ($use_branch=='develop') {
|
||||
$app_version = $app_version.'-pre';
|
||||
}
|
||||
|
||||
$full_app_version = $app_version.' - build '.$build_version.'-'.$hash_version;
|
||||
|
||||
|
||||
$array = var_export(
|
||||
array(
|
||||
'app_version' => $app_version,
|
||||
'full_app_version' => $full_app_version,
|
||||
'build_version' => $build_version,
|
||||
'prerelease_version' => $prerelease_version,
|
||||
'hash_version' => $hash_version,
|
||||
'full_hash' => $full_hash_version,
|
||||
'branch' => $git_branch),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Construct our file content
|
||||
$content = <<<CON
|
||||
<?php
|
||||
return $array;
|
||||
CON;
|
||||
|
||||
// And finally write the file and output the current version
|
||||
\File::put($versionFile, $content);
|
||||
$this->info('Setting NEW version: '. $full_app_version.' ('.$git_branch.')');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Versioning extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'versioning:update';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate and update app\'s version via git.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
|
||||
$versionFile = 'config/version.php';
|
||||
$hash_version = str_replace("\n", '', shell_exec('git describe --tags'));
|
||||
|
||||
$version = explode('-', $hash_version);
|
||||
|
||||
$array = var_export(
|
||||
array(
|
||||
'app_version' => $version[0],
|
||||
'build_version' => $version[1],
|
||||
'hash_version' => $version[2],
|
||||
'full_hash' => $hash_version),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
// Construct our file content
|
||||
$content = <<<CON
|
||||
<?php
|
||||
return $array;
|
||||
CON;
|
||||
|
||||
// And finally write the file and output the current version
|
||||
\File::put($versionFile, $content);
|
||||
$this->line('Setting version: '. config('version.app_version').' build '.config('version.build_version').' ('.config('version.hash_version').')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,30 +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\ObjectImportCommand::class,
|
||||
Commands\Versioning::class,
|
||||
Commands\SystemBackup::class,
|
||||
Commands\DisableLDAP::class,
|
||||
Commands\Purge::class,
|
||||
Commands\LdapSync::class,
|
||||
Commands\FixDoubleEscape::class,
|
||||
Commands\RecryptFromMcrypt::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
@@ -38,12 +22,15 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->command('snipeit:inventory-alerts')->daily();
|
||||
$schedule->command('snipeit:expiring-alerts')->daily();
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
21
app/Exceptions/CheckoutNotAllowed.php
Normal file
21
app/Exceptions/CheckoutNotAllowed.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
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 is_null($this->errorMessage) ? "A checkout is not allowed under these circumstances" : $this->errorMessage;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Log;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@@ -22,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,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -34,7 +36,10 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
parent::report($exception);
|
||||
if ($this->shouldReport($exception)) {
|
||||
Log::error($exception);
|
||||
return parent::report($exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,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', $e->response['messages'], $e->getMessage(), 400));
|
||||
}
|
||||
|
||||
if ($this->isHttpException($e)) {
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
@@ -80,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -113,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,99 +127,16 @@ class Helper
|
||||
$floatString = str_replace(",", "", $floatString);
|
||||
$floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString);
|
||||
// Strip Currency symbol
|
||||
$floatString = str_replace($LocaleInfo['currency_symbol'], '', $floatString);
|
||||
// If no currency symbol is set, default to $ because Murica
|
||||
$currencySymbol = $LocaleInfo['currency_symbol'];
|
||||
if (empty($currencySymbol)) {
|
||||
$currencySymbol = '$';
|
||||
}
|
||||
|
||||
$floatString = str_replace($currencySymbol, '', $floatString);
|
||||
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
|
||||
*
|
||||
@@ -229,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
|
||||
*
|
||||
@@ -283,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
|
||||
*
|
||||
@@ -324,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
|
||||
*
|
||||
@@ -400,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,9 +281,9 @@ class Helper
|
||||
*/
|
||||
public static function checkLowInventory()
|
||||
{
|
||||
$consumables = Consumable::with('users')->whereNotNull('min_amt')->get();
|
||||
$accessories = Accessory::with('users')->whereNotNull('min_amt')->get();
|
||||
$components = Component::with('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();
|
||||
@@ -466,7 +293,7 @@ class Helper
|
||||
$avail = $consumable->numRemaining();
|
||||
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
||||
if ($consumable->qty > 0) {
|
||||
$percent = number_format((($consumable->numRemaining() / $consumable->qty) * 100), 0);
|
||||
$percent = number_format((($avail / $consumable->qty) * 100), 0);
|
||||
} else {
|
||||
$percent = 100;
|
||||
}
|
||||
@@ -475,7 +302,7 @@ class Helper
|
||||
$items_array[$all_count]['name'] = $consumable->name;
|
||||
$items_array[$all_count]['type'] = 'consumables';
|
||||
$items_array[$all_count]['percent'] = $percent;
|
||||
$items_array[$all_count]['remaining']=$consumable->numRemaining();
|
||||
$items_array[$all_count]['remaining'] = $avail;
|
||||
$items_array[$all_count]['min_amt']=$consumable->min_amt;
|
||||
$all_count++;
|
||||
}
|
||||
@@ -484,11 +311,11 @@ class Helper
|
||||
}
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$avail = $accessory->numRemaining();
|
||||
$avail = $accessory->qty - $accessory->users_count;
|
||||
if ($avail < ($accessory->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
||||
|
||||
if ($accessory->qty > 0) {
|
||||
$percent = number_format((($accessory->numRemaining() / $accessory->qty) * 100), 0);
|
||||
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
||||
} else {
|
||||
$percent = 100;
|
||||
}
|
||||
@@ -497,7 +324,7 @@ class Helper
|
||||
$items_array[$all_count]['name'] = $accessory->name;
|
||||
$items_array[$all_count]['type'] = 'accessories';
|
||||
$items_array[$all_count]['percent'] = $percent;
|
||||
$items_array[$all_count]['remaining']=$accessory->numRemaining();
|
||||
$items_array[$all_count]['remaining'] = $avail;
|
||||
$items_array[$all_count]['min_amt']=$accessory->min_amt;
|
||||
$all_count++;
|
||||
}
|
||||
@@ -505,10 +332,10 @@ class Helper
|
||||
}
|
||||
|
||||
foreach ($components as $component) {
|
||||
$avail = $component->numRemaining();
|
||||
$avail = $component->qty - $component->assets_count;
|
||||
if ($avail < ($component->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
||||
if ($component->qty > 0) {
|
||||
$percent = number_format((($component->numRemaining() / $component->qty) * 100), 0);
|
||||
$percent = number_format((($avail / $component->qty) * 100), 0);
|
||||
} else {
|
||||
$percent = 100;
|
||||
}
|
||||
@@ -517,7 +344,7 @@ class Helper
|
||||
$items_array[$all_count]['name'] = $component->name;
|
||||
$items_array[$all_count]['type'] = 'components';
|
||||
$items_array[$all_count]['percent'] = $percent;
|
||||
$items_array[$all_count]['remaining']=$component->numRemaining();
|
||||
$items_array[$all_count]['remaining'] = $avail;
|
||||
$items_array[$all_count]['min_amt']=$component->min_amt;
|
||||
$all_count++;
|
||||
}
|
||||
@@ -684,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;
|
||||
@@ -725,5 +552,124 @@ class Helper
|
||||
}
|
||||
|
||||
|
||||
// Nicked from Drupal :)
|
||||
// Returns a file size limit in bytes based on the PHP upload_max_filesize
|
||||
// and post_max_size
|
||||
public static function file_upload_max_size() {
|
||||
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) {
|
||||
$max_size = $post_max_size;
|
||||
}
|
||||
|
||||
// If upload_max_size is less, then reduce. Except if upload_max_size is
|
||||
// zero, which indicates no limit.
|
||||
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
|
||||
if ($upload_max > 0 && $upload_max < $max_size) {
|
||||
$max_size = $upload_max;
|
||||
}
|
||||
}
|
||||
|
||||
return $max_size;
|
||||
}
|
||||
|
||||
public static function file_upload_max_size_readable() {
|
||||
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) {
|
||||
$max_size = ini_get('post_max_size');
|
||||
}
|
||||
|
||||
// If upload_max_size is less, then reduce. Except if upload_max_size is
|
||||
// zero, which indicates no limit.
|
||||
$upload_max = Helper::parse_size(ini_get('upload_max_filesize'));
|
||||
if ($upload_max > 0 && $upload_max < $max_size) {
|
||||
$max_size = ini_get('upload_max_filesize');
|
||||
}
|
||||
}
|
||||
return $max_size;
|
||||
}
|
||||
|
||||
public static function parse_size($size) {
|
||||
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
|
||||
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
|
||||
if ($unit) {
|
||||
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
|
||||
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
|
||||
}
|
||||
else {
|
||||
return round($size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,12 +12,13 @@ use DB;
|
||||
use Gate;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Mail;
|
||||
use Redirect;
|
||||
use Illuminate\Http\Request;
|
||||
use Slack;
|
||||
use Str;
|
||||
use View;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/** This controller handles all actions related to Accessories for
|
||||
* the Snipe-IT Asset Management application.
|
||||
@@ -52,13 +53,9 @@ class AccessoriesController extends Controller
|
||||
public function create(Request $request)
|
||||
{
|
||||
$this->authorize('create', Accessory::class);
|
||||
// Show the page
|
||||
return view('accessories/edit')
|
||||
->with('item', new Accessory)
|
||||
->with('category_list', Helper::categoryList('accessory'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
$category_type = 'accessory';
|
||||
return view('accessories/edit')->with('category_type', $category_type)
|
||||
->with('item', new Accessory);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +65,7 @@ class AccessoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize(Accessory::class);
|
||||
// create a new model instance
|
||||
@@ -87,6 +84,9 @@ class AccessoriesController extends Controller
|
||||
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->user_id = Auth::user()->id;
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
|
||||
|
||||
|
||||
// Was the accessory created?
|
||||
if ($accessory->save()) {
|
||||
@@ -105,18 +105,15 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function edit(Request $request, $accessoryId = null)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($item = Accessory::find($accessoryId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
|
||||
if ($item = Accessory::find($accessoryId)) {
|
||||
$this->authorize($item);
|
||||
$category_type = 'accessory';
|
||||
return view('accessories/edit', compact('item'))->with('category_type', $category_type);
|
||||
}
|
||||
|
||||
$this->authorize($item);
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
|
||||
return view('accessories/edit', compact('item'))
|
||||
->with('category_list', Helper::categoryList('accessory'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +124,7 @@ class AccessoriesController extends Controller
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function update(Request $request, $accessoryId = null)
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null)
|
||||
{
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
@@ -144,11 +141,15 @@ class AccessoriesController extends Controller
|
||||
$accessory->manufacturer_id = request('manufacturer_id');
|
||||
$accessory->order_number = request('order_number');
|
||||
$accessory->model_number = request('model_number');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = request('purchase_cost');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = request('purchase_cost');
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
// Was the accessory updated?
|
||||
$accessory = $request->handleImages($accessory,600, public_path().'/uploads/accessories');
|
||||
|
||||
|
||||
// Was the accessory updated?
|
||||
if ($accessory->save()) {
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
@@ -197,11 +198,7 @@ class AccessoriesController extends Controller
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/accessories/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('accessories')->with('error', $error);
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,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'))->with('users_list', Helper::usersList());
|
||||
|
||||
}
|
||||
|
||||
@@ -247,7 +251,7 @@ class AccessoriesController extends Controller
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
if (!$user = User::find(Input::get('assigned_to'))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
return redirect()->route('checkout/accessory', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
|
||||
}
|
||||
|
||||
// Update the accessory data
|
||||
@@ -260,7 +264,7 @@ class AccessoriesController extends Controller
|
||||
'assigned_to' => $request->get('assigned_to')
|
||||
]);
|
||||
|
||||
$logaction = $accessory->logCheckout(e(Input::get('note')));
|
||||
$logaction = $accessory->logCheckout(e(Input::get('note')), $user);
|
||||
|
||||
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
|
||||
|
||||
@@ -273,15 +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())) {
|
||||
|
||||
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'));
|
||||
@@ -328,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);
|
||||
@@ -346,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')) {
|
||||
|
||||
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'));
|
||||
}
|
||||
@@ -369,143 +356,5 @@ class AccessoriesController extends Controller
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the JSON response for accessories listing view.
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
* "actions": "(links to available actions)",
|
||||
* "category": "(link to category)",
|
||||
* "company": "My Company",
|
||||
* "location": "My Location",
|
||||
* "min_amt": 2,
|
||||
* "name": "(link to accessory),
|
||||
* "numRemaining": 6,
|
||||
* "order_number": null,
|
||||
* "purchase_cost": "0.00",
|
||||
* "purchase_date": null,
|
||||
* "qty": 7
|
||||
* },
|
||||
*
|
||||
* The names of the fields in the returns JSON correspond directly to the the
|
||||
* names of the fields in the bootstrap-tables in the view.
|
||||
*
|
||||
* For debugging, see at /api/accessories/list
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @return string JSON containing accessories and their associated atrributes.
|
||||
* @internal param int $accessoryId
|
||||
*/
|
||||
public function getDatatable(Request $request)
|
||||
{
|
||||
$this->authorize('index', Accessory::class);
|
||||
$accessories = Company::scopeCompanyables(
|
||||
Accessory::select('accessories.*')
|
||||
->whereNull('accessories.deleted_at')
|
||||
->with('category', 'company', 'manufacturer', 'users', 'location')
|
||||
);
|
||||
if (Input::has('search')) {
|
||||
$accessories = $accessories->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$allowed_columns = ['name','min_amt','order_number','purchase_date','purchase_cost','company','category','model_number', 'manufacturer', 'location'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$accessories = $accessories->OrderCategory($order);
|
||||
break;
|
||||
case 'company':
|
||||
$accessories = $accessories->OrderCompany($order);
|
||||
break;
|
||||
case 'location':
|
||||
$accessories = $accessories->OrderLocation($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$accessories = $accessories->OrderManufacturer($order);
|
||||
break;
|
||||
default:
|
||||
$accessories = $accessories->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$accessCount = $accessories->count();
|
||||
$accessories = $accessories->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$rows[] = $accessory->present()->forDataTable();
|
||||
}
|
||||
|
||||
$data = array('total'=>$accessCount, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON response for accessory detail view.
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* {
|
||||
* "rows": [
|
||||
* {
|
||||
* "actions": "(link to available actions)",
|
||||
* "name": "(link to user)"
|
||||
* }
|
||||
* ],
|
||||
* "total": 1
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* The names of the fields in the returns JSON correspond directly to the the
|
||||
* names of the fields in the bootstrap-tables in the view.
|
||||
*
|
||||
* For debugging, see at /api/accessories/$accessoryID/view
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return string JSON containing accessories and their associated atrributes.
|
||||
**/
|
||||
public function getDataView(Request $request, $accessoryID)
|
||||
{
|
||||
$accessory = Accessory::find($accessoryID);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
|
||||
$accessory_users = $accessory->users;
|
||||
$count = $accessory_users->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($accessory_users as $user) {
|
||||
$actions = '';
|
||||
if (Gate::allows('checkin', $accessory)) {
|
||||
$actions .= Helper::generateDatatableButton('checkin', route('checkin/accessory', $user->pivot->id));
|
||||
}
|
||||
|
||||
if (Gate::allows('view', $user)) {
|
||||
$name = (string) link_to_route('users.show', e($user->present()->fullName()), [$user->id]);
|
||||
} else {
|
||||
$name = e($user->present()->fullName());
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'name' => $name,
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total'=>$count, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ use App\Http\Controllers\Controller;
|
||||
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
|
||||
{
|
||||
@@ -23,22 +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('manufacturer_id')) {
|
||||
if ($request->filled('category_id')) {
|
||||
$accessories->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$accessories->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
if ($request->filled('supplier_id')) {
|
||||
$accessories->where('supplier_id','=',$request->input('supplier_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 = (($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';
|
||||
|
||||
@@ -95,8 +113,6 @@ class AccessoriesController extends Controller
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
$accessory_users = $accessory->users;
|
||||
|
||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||
}
|
||||
|
||||
@@ -128,10 +144,15 @@ class AccessoriesController extends Controller
|
||||
public function checkedout($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id)->with('users')->first();
|
||||
$accessories_users = $accessory->users;
|
||||
$total = $accessories_users->count();
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessories($accessories_users, $total);
|
||||
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
if (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
$accessory_users = $accessory->users;
|
||||
$total = $accessory_users->count();
|
||||
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_users, $total);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,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')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\AssetMaintenance;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Company;
|
||||
use App\Models\Asset;
|
||||
use App\Helpers\Helper;
|
||||
use Auth;
|
||||
use Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\AssetMaintenancesTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetMaintenance;
|
||||
use App\Models\Company;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Asset Maintenance for
|
||||
@@ -39,10 +40,31 @@ class AssetMaintenancesController extends Controller
|
||||
$maintenances = $maintenances->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
if ($request->filled('asset_id')) {
|
||||
$maintenances->where('asset_id', '=', $request->input('asset_id'));
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','title','asset_maintenance_time','asset_maintenance_type','cost','start_date','completion_date','notes','user_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 = (($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',
|
||||
'title',
|
||||
'asset_maintenance_time',
|
||||
'asset_maintenance_type',
|
||||
'cost',
|
||||
'start_date',
|
||||
'completion_date',
|
||||
'notes',
|
||||
'asset_tag',
|
||||
'asset_name',
|
||||
'user_id'
|
||||
];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
@@ -50,16 +72,20 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
$total = $maintenances->count();
|
||||
$maintenances = $maintenances->skip($offset)->take($limit)->get();
|
||||
|
||||
|
||||
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $maintenances->count());
|
||||
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Helpers\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,19 +30,43 @@ class AssetModelsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer'];
|
||||
$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'])
|
||||
$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('search')) {
|
||||
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$assetmodels->onlyTrashed();
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels->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 = (($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');
|
||||
|
||||
$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') : 'models.created_at';
|
||||
|
||||
@@ -55,6 +80,7 @@ class AssetModelsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
|
||||
$total = $assetmodels->count();
|
||||
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
|
||||
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
|
||||
@@ -93,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);
|
||||
}
|
||||
|
||||
@@ -124,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()));
|
||||
@@ -154,8 +180,69 @@ class AssetModelsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.assoc_users')));
|
||||
}
|
||||
|
||||
if ($assetmodel->image) {
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$assetmodel->image);
|
||||
} catch (\Exception $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')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$assetmodels = AssetModel::select([
|
||||
'models.id',
|
||||
'models.name',
|
||||
'models.image',
|
||||
'models.model_number',
|
||||
'models.manufacturer_id',
|
||||
'models.category_id',
|
||||
])->with('manufacturer','category');
|
||||
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels = $assetmodels->SearchByManufacturerOrCat($request->input('search'));
|
||||
}
|
||||
|
||||
$assetmodels = $assetmodels->OrderCategory('ASC')->OrderManufacturer('ASC')->orderby('models.name', 'asc')->orderby('models.model_number', 'asc')->paginate(50);
|
||||
|
||||
foreach ($assetmodels as $assetmodel) {
|
||||
|
||||
$assetmodel->use_text = '';
|
||||
|
||||
if ($settings->modellistCheckedValue('category')) {
|
||||
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
|
||||
}
|
||||
|
||||
if ($settings->modellistCheckedValue('manufacturer')) {
|
||||
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
|
||||
}
|
||||
|
||||
$assetmodel->use_text .= e($assetmodel->name);
|
||||
|
||||
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
|
||||
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
|
||||
}
|
||||
|
||||
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? url('/').'/uploads/models/'.$assetmodel->image : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($assetmodels);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetRequest;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
@@ -30,6 +31,7 @@ use Str;
|
||||
use TCPDF;
|
||||
use Validator;
|
||||
use View;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
|
||||
/**
|
||||
@@ -50,9 +52,11 @@ 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);
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
@@ -69,102 +73,195 @@ class AssetsController extends Controller
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'purchase_date',
|
||||
'purchase_cost'
|
||||
'purchase_cost',
|
||||
'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
|
||||
foreach ($all_custom_fields as $field) {
|
||||
$allowed_columns[]=$field->db_column_name();
|
||||
}
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'))->with(
|
||||
'assetLoc', 'assetstatus', 'defaultLoc', 'assetlog', 'company',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'assigneduser','supplier');
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
// If we should search on everything
|
||||
if (($request->has('search')) && (count($filter) == 0)) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
// otherwise loop through the filters and search strictly on them
|
||||
} else {
|
||||
if (count($filter) > 0) {
|
||||
$assets->ByFilter($filter);
|
||||
|
||||
// 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->filled('status_id')) {
|
||||
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
||||
}
|
||||
|
||||
if ($request->input('requestable')=='true') {
|
||||
$assets->where('assets.requestable', '=', '1');
|
||||
}
|
||||
|
||||
if ($request->filled('model_id')) {
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
$assets->InCategory($request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$assets->where('assets.location_id', '=', $request->input('location_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->filled('assigned_to')) && ($request->filled('assigned_type'))) {
|
||||
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
|
||||
->where('assets.assigned_type', '=', $request->input('assigned_type'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$assets->where('assets.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$assets->ByManufacturer($request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation_id')) {
|
||||
$assets->ByDepreciationId($request->input('depreciation_id'));
|
||||
}
|
||||
|
||||
$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');
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// These are used by the API to query against specific ID numbers
|
||||
if ($request->has('status_id')) {
|
||||
$assets->where('status_id', '=', $request->input('status_id'));
|
||||
}
|
||||
|
||||
if ($request->has('model_id')) {
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
}
|
||||
|
||||
if ($request->has('category_id')) {
|
||||
$assets->InCategory($request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
$assets->ByLocationId($request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->has('supplier_id')) {
|
||||
$assets->where('assets.supplier_id', '=', $request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
$assets->where('assets.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
$assets->ByManufacturer($request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
$request->has('order_number') ? $assets = $assets->where('order_number', '=', e($request->get('order_number'))) : '';
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
// This is used by the sidenav, mostly
|
||||
|
||||
// We switched from using query scopes here because of a Laravel bug
|
||||
// related to fulltext searches on complex queries.
|
||||
// I am sad. :(
|
||||
switch ($request->input('status')) {
|
||||
case 'Deleted':
|
||||
$assets->withTrashed()->Deleted();
|
||||
break;
|
||||
case 'Pending':
|
||||
$assets->Pending();
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',0)
|
||||
->where('status_alias.pending','=',1)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
break;
|
||||
case 'RTD':
|
||||
$assets->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);
|
||||
});
|
||||
break;
|
||||
case 'Undeployable':
|
||||
$assets->Undeployable();
|
||||
break;
|
||||
case 'Archived':
|
||||
$assets->Archived();
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',0)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 1);
|
||||
});
|
||||
break;
|
||||
case 'Requestable':
|
||||
$assets->RequestableAssets();
|
||||
$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);
|
||||
});
|
||||
|
||||
break;
|
||||
case 'Deployed':
|
||||
$assets->Deployed();
|
||||
// more sad, horrible workarounds for laravel bugs when doing full text searches
|
||||
$assets->where('assets.assigned_to', '>', '0');
|
||||
break;
|
||||
default:
|
||||
|
||||
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
|
||||
} else {
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ((!is_null($filter)) && (count($filter)) > 0) {
|
||||
$assets->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
// This handles all of the pivot sorting (versus the assets.* fields in the allowed_columns array)
|
||||
$column_sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets.created_at';
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
// This is kinda gross, but we need to do this because the Bootstrap Tables
|
||||
// API passes custom field ordering as custom_fields.fieldname, and we have to strip
|
||||
// that out to let the default sorter below order them correctly on the assets table.
|
||||
$sort_override = str_replace('custom_fields.','', $request->input('sort')) ;
|
||||
|
||||
// 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);
|
||||
break;
|
||||
@@ -182,6 +279,8 @@ class AssetsController extends Controller
|
||||
break;
|
||||
case 'location':
|
||||
$assets->OrderLocation($order);
|
||||
case 'rtd_location':
|
||||
$assets->OrderRtdLocation($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$assets->OrderStatus($order);
|
||||
@@ -196,14 +295,54 @@ class AssetsController extends Controller
|
||||
$assets->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* 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>]
|
||||
@@ -213,12 +352,69 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
if ($asset = Asset::withTrashed()->find($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);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select([
|
||||
'assets.id',
|
||||
'assets.name',
|
||||
'assets.asset_tag',
|
||||
'assets.model_id',
|
||||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id'
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(),'company_id', 'assets');
|
||||
|
||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$assets = $assets->AssignedSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
$assets = $assets->paginate(50);
|
||||
|
||||
// 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 ($assets as $asset) {
|
||||
|
||||
|
||||
$asset->use_text = $asset->present()->fullName;
|
||||
|
||||
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType()=='pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
}
|
||||
|
||||
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($assets);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +440,7 @@ class AssetsController extends Controller
|
||||
$asset->model_id = $request->get('model_id');
|
||||
$asset->order_number = $request->get('order_number');
|
||||
$asset->notes = $request->get('notes');
|
||||
$asset->asset_tag = $request->get('asset_tag');
|
||||
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
|
||||
$asset->user_id = Auth::id();
|
||||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
@@ -257,18 +453,25 @@ class AssetsController extends Controller
|
||||
$asset->supplier_id = $request->get('supplier_id', 0);
|
||||
$asset->requestable = $request->get('requestable', 0);
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id', null);
|
||||
$asset->location_id = $request->get('rtd_location_id', null);
|
||||
|
||||
// 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()));
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
$asset->logCreate();
|
||||
|
||||
if ($request->get('assigned_user')) {
|
||||
$target = User::find(request('assigned_user'));
|
||||
} elseif ($request->get('assigned_asset')) {
|
||||
@@ -296,66 +499,51 @@ 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('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
|
||||
if ($request->has('model_id')) {
|
||||
if (($model = AssetModel::find($request->get('model_id'))) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($request->has($field->convertUnicodeDbSlug())) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = e($request->input($field->convertUnicodeDbSlug()));
|
||||
$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())) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
|
||||
if ($request->get('assigned_user')) {
|
||||
$target = User::find(request('assigned_user'));
|
||||
} elseif ($request->get('assigned_asset')) {
|
||||
$target = Asset::find(request('assigned_asset'));
|
||||
} elseif ($request->get('assigned_location')) {
|
||||
$target = Location::find(request('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;
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')));
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
@@ -404,7 +592,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function checkout(Request $request, $asset_id)
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
@@ -415,29 +603,64 @@ class AssetsController extends Controller
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if ($request->has('user_id')) {
|
||||
$target = User::find($request->input('user_id'));
|
||||
} elseif ($request->has('asset_id')) {
|
||||
$target = Asset::find($request->input('asset_id'));
|
||||
} elseif ($request->has('location_id')) {
|
||||
$target = Location::find($request->input('location_id'));
|
||||
$error_payload = [];
|
||||
$error_payload['asset'] = [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
];
|
||||
|
||||
|
||||
// This item is checked out to a location
|
||||
if (request('checkout_to_type')=='location') {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
$asset->location_id = ($target) ? $target->id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_location');
|
||||
$error_payload['target_type'] = 'location';
|
||||
|
||||
} elseif (request('checkout_to_type')=='asset') {
|
||||
$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
|
||||
$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) && (isset($target->location_id))) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_user');
|
||||
$error_payload['target_type'] = 'user';
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!isset($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], 'No valid checkout target specified for asset '.e($asset->asset_tag).'.'));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
$checkout_at = request('checkout_at', date("Y-m-d H:i:s"));
|
||||
$expected_checkin = request('expected_checkin', null);
|
||||
$note = request('note', null);
|
||||
$asset_name = request('name', null);
|
||||
|
||||
|
||||
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name)) {
|
||||
// Set the location ID to the RTD location id if there is one
|
||||
// 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')));
|
||||
}
|
||||
|
||||
|
||||
@@ -449,7 +672,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function checkin($asset_id)
|
||||
public function checkin(Request $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
@@ -466,32 +689,23 @@ class AssetsController extends Controller
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
$asset->name = e(Input::get('name'));
|
||||
|
||||
if (Input::has('status_id')) {
|
||||
$asset->status_id = e(Input::get('status_id'));
|
||||
if ($request->filled('name')) {
|
||||
$asset->name = $request->input('name');
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
if (Input::has('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;
|
||||
|
||||
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')));
|
||||
}
|
||||
|
||||
@@ -507,23 +721,91 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function audit(Request $request, $id) {
|
||||
public function audit(Request $request) {
|
||||
|
||||
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
||||
$rules = array(
|
||||
'id' => 'required'
|
||||
'asset_tag' => 'required',
|
||||
'location_id' => 'exists:locations,id|nullable|numeric',
|
||||
'next_audit_date' => 'date|nullable'
|
||||
);
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
|
||||
$asset = Asset::findOrFail($id);
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
|
||||
if ($asset->save()) {
|
||||
$asset->logAudit(request('note'));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.audit.success')));
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
||||
}
|
||||
|
||||
$asset = Asset::where('asset_tag','=', $request->input('asset_tag'))->first();
|
||||
|
||||
|
||||
if ($asset) {
|
||||
// We don't want to log this as a normal update, so let's bypass that
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
$asset->last_audit_date = date('Y-m-d h:i:s');
|
||||
|
||||
if ($asset->save()) {
|
||||
$log = $asset->logAudit(request('note'),request('location_id'));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'note'=> e($request->input('note')),
|
||||
'next_audit_date' => Helper::getFormattedDateObject($log->calcNextAuditDate())
|
||||
], trans('admin/hardware/message.audit.success')));
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found'));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Category;
|
||||
use App\Http\Transformers\CategoriesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
class CategoriesController extends Controller
|
||||
{
|
||||
@@ -20,19 +21,24 @@ class CategoriesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
$allowed_columns = ['id', 'name','category_type','use_default_eula','require_acceptance','checkin_email'];
|
||||
$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','require_acceptance','checkin_email'])
|
||||
->withCount('assets', 'accessories', 'consumables', 'components');
|
||||
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email','image'])
|
||||
->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') : 'created_at';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
|
||||
$categories->orderBy($sort, $order);
|
||||
|
||||
$total = $categories->count();
|
||||
@@ -91,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());
|
||||
|
||||
@@ -128,4 +134,40 @@ class CategoriesController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request, $category_type = 'asset')
|
||||
{
|
||||
|
||||
$categories = Category::select([
|
||||
'id',
|
||||
'name',
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$categories = $categories->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$categories = $categories->where('category_type', $category_type)->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
// 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 ($categories as $category) {
|
||||
$category->use_image = ($category->image) ? url('/').'/uploads/categories/'.$category->image : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($categories);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Company;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
class CompaniesController extends Controller
|
||||
{
|
||||
@@ -21,19 +22,32 @@ class CompaniesController extends Controller
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
$allowed_columns = ['id','name'];
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
'assets_count',
|
||||
'licenses_count',
|
||||
'accessories_count',
|
||||
'consumables_count',
|
||||
'components_count',
|
||||
];
|
||||
|
||||
$companies = Company::withCount('assets','licenses','accessories','consumables','components','users')
|
||||
->withCount('users')->withCount('users')->withCount('assets')
|
||||
->withCount('licenses')->withCount('accessories')
|
||||
->withCount('consumables')->withCount('components');
|
||||
$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);
|
||||
@@ -95,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());
|
||||
|
||||
@@ -141,4 +155,37 @@ class CompaniesController extends Controller
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$companies = Company::select([
|
||||
'companies.id',
|
||||
'companies.name',
|
||||
'companies.image',
|
||||
]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$companies = $companies->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
// 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 ($companies as $company) {
|
||||
$company->use_image = ($company->image) ? url('/').'/uploads/companies/'.$company->image : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($companies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,17 +24,33 @@ 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'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
if ($request->filled('company_id')) {
|
||||
$components->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location'];
|
||||
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';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -89,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')));
|
||||
}
|
||||
|
||||
|
||||
@@ -110,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());
|
||||
|
||||
@@ -149,7 +163,7 @@ class ComponentsController extends Controller
|
||||
*/
|
||||
public function getAssets(Request $request, $id)
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
|
||||
$component = Component::findOrFail($id);
|
||||
$assets = $component->assets();
|
||||
@@ -158,6 +172,6 @@ class ComponentsController extends Controller
|
||||
$limit = $request->input('limit', 50);
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new ComponentsAssetsTransformer)->transformAssets($assets, $total);
|
||||
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,26 +24,34 @@ 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);
|
||||
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty'];
|
||||
// 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());
|
||||
|
||||
@@ -148,4 +156,44 @@ class ConsumablesController extends Controller
|
||||
$consumable->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.delete.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON response containing details on the users associated with this consumable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::getView() method that returns the form.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return array
|
||||
*/
|
||||
public function getDataView($consumableId)
|
||||
{
|
||||
$consumable = Consumable::with(array('consumableAssignments'=>
|
||||
function ($query) {
|
||||
$query->orderBy($query->getModel()->getTable().'.created_at', 'DESC');
|
||||
},
|
||||
'consumableAssignments.admin'=> function ($query) {
|
||||
},
|
||||
'consumableAssignments.user'=> function ($query) {
|
||||
},
|
||||
))->find($consumableId);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
$this->authorize('view', Consumable::class);
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumable->consumableAssignments as $consumable_assignment) {
|
||||
$rows[] = [
|
||||
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
|
||||
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
|
||||
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
|
||||
];
|
||||
}
|
||||
|
||||
$consumableCount = $consumable->users->count();
|
||||
$data = array('total' => $consumableCount, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\CustomFieldsTransformer;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\CustomFieldset;
|
||||
use Illuminate\Http\Request;
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CustomFieldsController extends Controller
|
||||
{
|
||||
@@ -16,9 +21,100 @@ class CustomFieldsController extends Controller
|
||||
* @since [v3.0]
|
||||
* @return Array
|
||||
*/
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomField::class);
|
||||
$fields = CustomField::get();
|
||||
return (new CustomFieldsTransformer)->transformCustomFields($fields, $fields->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the given field
|
||||
* @author [V. Cordes] [<volker@fdatek.de>]
|
||||
* @param int $id
|
||||
* @since [v4.1.10]
|
||||
* @return View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', CustomField::class);
|
||||
if ($field = CustomField::find($id)) {
|
||||
return (new CustomFieldsTransformer)->transformCustomField($field);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/custom_fields/message.field.invalid')), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified field
|
||||
*
|
||||
* @author [V. Cordes] [<volker@fdatek.de>]
|
||||
* @since [v4.1.10]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', CustomField::class);
|
||||
$field = CustomField::findOrFail($id);
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
|
||||
}
|
||||
|
||||
$field->fill($data);
|
||||
|
||||
if ($field->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a newly created field.
|
||||
*
|
||||
* @author [V. Cordes] [<volker@fdatek.de>]
|
||||
* @since [v4.1.10]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
$field = new CustomField;
|
||||
|
||||
$data = $request->all();
|
||||
$validator = Validator::make($data, $field->validationRules());
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
|
||||
}
|
||||
$field->fill($data);
|
||||
|
||||
if ($field->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $field, trans('admin/custom_fields/message.field.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $field->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
public function postReorder(Request $request, $id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('update', $fieldset);
|
||||
|
||||
$fields = array();
|
||||
$order_array = array();
|
||||
|
||||
@@ -36,6 +132,40 @@ class CustomFieldsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
public function associate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
foreach ($field->fieldset as $fieldset) {
|
||||
if ($fieldset->id == $fieldset_id) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
|
||||
}
|
||||
}
|
||||
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
$fieldset->fields()->attach($field->id, ["required" => ($request->input('required') == "on"), "order" => $request->input('order', $fieldset->fields->count())]);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
|
||||
}
|
||||
|
||||
public function disassociate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
foreach ($field->fieldset as $fieldset) {
|
||||
if ($fieldset->id == $fieldset_id) {
|
||||
$fieldset->fields()->detach($field->id);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
|
||||
}
|
||||
}
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a custom field.
|
||||
@@ -46,15 +176,17 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function destroy($field_id)
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
$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.'));
|
||||
} else {
|
||||
$field->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
$field->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -126,7 +124,7 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$this->authorize('delete', CustomFieldset::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
|
||||
|
||||
$modelsCount = $fieldset->models->count();
|
||||
$fieldsCount = $fieldset->fields->count();
|
||||
|
||||
@@ -141,7 +139,41 @@ class CustomFieldsetsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Unspecified error'));
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of fields belonging to a fieldset.
|
||||
*
|
||||
* @author [V. Cordes] [<volker@fdatek.de>]
|
||||
* @since [v4.1.10]
|
||||
* @param $fieldsetId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function fields($id)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
$set = CustomFieldset::findOrFail($id);
|
||||
$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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,40 +8,58 @@ use App\Models\Department;
|
||||
use App\Http\Transformers\DepartmentsTransformer;
|
||||
use App\Helpers\Helper;
|
||||
use Auth;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
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'];
|
||||
$allowed_columns = ['id','name','image','users_count'];
|
||||
|
||||
$departments = Department::select([
|
||||
'id',
|
||||
'name',
|
||||
'location_id',
|
||||
'company_id',
|
||||
'manager_id',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
])->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();
|
||||
@@ -63,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')));
|
||||
@@ -101,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')));
|
||||
}
|
||||
@@ -110,4 +130,61 @@ class DepartmentsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$departments = Department::select([
|
||||
'id',
|
||||
'name',
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$departments = $departments->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
// 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 ($departments as $department) {
|
||||
$department->use_image = ($department->image) ? url('/').'/uploads/departments/'.$department->image : null;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
|
||||
@@ -20,16 +20,21 @@ class GroupsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Group::class);
|
||||
$allowed_columns = ['id','name','created_at'];
|
||||
$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);
|
||||
@@ -73,7 +78,7 @@ class GroupsController extends Controller
|
||||
{
|
||||
$this->authorize('view', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
return $group;
|
||||
return (new GroupsTransformer)->transformGroup($group);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ use Illuminate\Support\Facades\Input;
|
||||
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
|
||||
{
|
||||
@@ -23,7 +25,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
return (new ImportsTransformer)->transformImports($imports);
|
||||
|
||||
@@ -37,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 = [];
|
||||
@@ -56,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 {
|
||||
@@ -70,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;
|
||||
}
|
||||
@@ -93,7 +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
|
||||
|
||||
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')) {
|
||||
@@ -134,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,11 +2,15 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\LicenseSeatsTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Models\License;
|
||||
use App\Models\Company;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LicensesController extends Controller
|
||||
{
|
||||
@@ -21,80 +25,97 @@ class LicensesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer'));
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats as free_seats_count'));
|
||||
|
||||
if ($request->has('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$licenses = $licenses->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 = (($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';
|
||||
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'manufacturer':
|
||||
$licenses = $licenses->OrderManufacturer($order);
|
||||
case 'manufacturer':
|
||||
$licenses = $licenses->leftJoin('manufacturers', 'licenses.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$licenses = $licenses->OrderSupplier($order);
|
||||
$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->OrderCompany($order);
|
||||
$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'];
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$total = $licenses->count();
|
||||
|
||||
$licenses = $licenses->skip($offset)->take($limit)->get();
|
||||
return (new LicensesTransformer)->transformLicenses($licenses, $total);
|
||||
|
||||
@@ -114,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()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,5 +196,66 @@ 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')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license seat listing
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function seats(Request $request, $licenseId)
|
||||
{
|
||||
|
||||
if ($license = License::find($licenseId)) {
|
||||
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::where('license_seats.license_id', $licenseId)
|
||||
->with('license', 'user', 'asset', 'user.department');
|
||||
|
||||
$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) {
|
||||
return (new LicenseSeatsTransformer)->transformLicenseSeats($seats, $total);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ use App\Http\Controllers\Controller;
|
||||
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
|
||||
{
|
||||
@@ -20,10 +23,12 @@ class LocationsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$allowed_columns = ['id','name','address','address2','city','state','country','zip','created_at',
|
||||
'updated_at','parent_id', 'manager_id'];
|
||||
$allowed_columns = [
|
||||
'id','name','address','address2','city','state','country','zip','created_at',
|
||||
'updated_at','manager_id','image',
|
||||
'assigned_assets_count','users_count','assets_count','currency'];
|
||||
|
||||
$locations = Location::select([
|
||||
$locations = Location::with('parent', 'manager', 'children')->select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.address',
|
||||
@@ -36,23 +41,43 @@ class LocationsController extends Controller
|
||||
'locations.manager_id',
|
||||
'locations.created_at',
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency'
|
||||
])->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'));
|
||||
}
|
||||
|
||||
$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 = (($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');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$locations->orderBy($sort, $order);
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'parent':
|
||||
$locations->OrderParent($order);
|
||||
break;
|
||||
case 'manager':
|
||||
$locations->OrderManager($order);
|
||||
break;
|
||||
default:
|
||||
$locations->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$total = $locations->count();
|
||||
$locations = $locations->skip($offset)->take($limit)->get();
|
||||
return (new LocationsTransformer)->transformLocations($locations, $total);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +99,6 @@ class LocationsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,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);
|
||||
}
|
||||
|
||||
@@ -104,12 +147,25 @@ 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()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.update.success')));
|
||||
return response()->json(
|
||||
Helper::formatStandardApiResponse(
|
||||
'success',
|
||||
(new LocationsTransformer)->transformLocation($location),
|
||||
trans('admin/locations/message.update.success')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
|
||||
@@ -129,7 +185,82 @@ class LocationsController extends Controller
|
||||
$location = Location::findOrFail($id);
|
||||
$this->authorize('delete', $location);
|
||||
$location->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/locations/message.delete.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$locations = Location::select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.parent_id',
|
||||
'locations.image',
|
||||
]);
|
||||
|
||||
$page = 1;
|
||||
if ($request->filled('page')) {
|
||||
$page = $request->input('page');
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$locations = $locations->where('locations.name', 'LIKE', '%'.$request->input('search').'%');
|
||||
}
|
||||
|
||||
$locations = $locations->orderBy('name', 'ASC')->get();
|
||||
|
||||
$locations_with_children = [];
|
||||
|
||||
foreach ($locations as $location) {
|
||||
if (!array_key_exists($location->parent_id, $locations_with_children)) {
|
||||
$locations_with_children[$location->parent_id] = [];
|
||||
}
|
||||
$locations_with_children[$location->parent_id][] = $location;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Helpers\Helper;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Http\Transformers\DatatablesTransformer;
|
||||
use App\Http\Transformers\ManufacturersTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
class ManufacturersController extends Controller
|
||||
{
|
||||
@@ -21,19 +22,28 @@ class ManufacturersController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$allowed_columns = ['id','name','url','support_url','support_email','support_phone','created_at','updated_at'];
|
||||
$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')
|
||||
)->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);
|
||||
@@ -76,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);
|
||||
}
|
||||
|
||||
@@ -92,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());
|
||||
|
||||
@@ -120,4 +130,39 @@ class ManufacturersController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$manufacturers = Manufacturer::select([
|
||||
'id',
|
||||
'name',
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$manufacturers = $manufacturers->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
// 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 ($manufacturers as $manufacturer) {
|
||||
$manufacturer->use_text = $manufacturer->name;
|
||||
$manufacturer->use_image = ($manufacturer->image) ? url('/').'/uploads/manufacturers/'.$manufacturer->image : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($manufacturers);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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,43 +18,49 @@ class ReportsController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target','location');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target');
|
||||
|
||||
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'
|
||||
'created_at',
|
||||
'target_id',
|
||||
'user_id',
|
||||
'action_type',
|
||||
'note'
|
||||
];
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
$total = $actionlogs->count();
|
||||
$actionlogs = $actionlogs->orderBy($sort, $order);
|
||||
$actionlogs = $actionlogs->skip($offset)->take($limit)->get();
|
||||
return (new ActionlogsTransformer)->transformActionlogs($actionlogs, $total);
|
||||
|
||||
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
|
||||
|
||||
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,78 +5,25 @@ namespace App\Http\Controllers\Api;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
|
||||
|
||||
public function ldaptest()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->ldap_enabled!='1') {
|
||||
\Log::debug('LDAP is not enabled so cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, so we cannot test LDAP connections.'], 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getLdapTest()
|
||||
{
|
||||
\Log::debug('Preparing to test LDAP connection');
|
||||
|
||||
try {
|
||||
@@ -85,6 +32,60 @@ class SettingsController extends Controller
|
||||
\Log::debug('attempting to bind to LDAP for LDAP test');
|
||||
Ldap::bindAdminToLdap($connection);
|
||||
return response()->json(['message' => 'It worked!'], 200);
|
||||
} catch (\Exception $e) {
|
||||
\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::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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function ldaptestlogin(Request $request)
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
$rules = array(
|
||||
'ldaptest_user' => 'required',
|
||||
'ldaptest_password' => 'required'
|
||||
);
|
||||
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
\Log::debug('LDAP Validation test failed.');
|
||||
$validation_errors = implode(' ',$validator->errors()->all());
|
||||
return response()->json(['message' => $validator->errors()->all()], 400);
|
||||
}
|
||||
|
||||
|
||||
\Log::debug('Preparing to test LDAP login');
|
||||
try {
|
||||
$connection = Ldap::connectToLdap();
|
||||
try {
|
||||
Ldap::bindAdminToLdap($connection);
|
||||
\Log::debug('Attempting to bind to LDAP for LDAP test');
|
||||
try {
|
||||
$ldap_user = Ldap::findAndBindUserLdap($request->input('ldaptest_user'), $request->input('ldaptest_password'));
|
||||
if ($ldap_user) {
|
||||
\Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
|
||||
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
|
||||
}
|
||||
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('LDAP login failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Bind failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
@@ -92,10 +93,56 @@ class SettingsController extends Controller
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed');
|
||||
return response()->json(['message' => $e->getMessage()], 600);
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function ajaxTestEmail()
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return response()->json(['message' => 'Mail would have been sent, but this application is in demo mode! '], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,16 +22,21 @@ class StatuslabelsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$allowed_columns = ['id','name','created_at'];
|
||||
$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.'));
|
||||
}
|
||||
|
||||
@@ -137,8 +142,14 @@ class StatuslabelsController extends Controller
|
||||
$this->authorize('delete', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
$this->authorize('delete', $statuslabel);
|
||||
$statuslabel->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
|
||||
|
||||
// Check that there are no assets associated
|
||||
if ($statuslabel->assets()->count() == 0) {
|
||||
$statuslabel->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets')));
|
||||
|
||||
}
|
||||
|
||||
@@ -154,23 +165,25 @@ class StatuslabelsController extends Controller
|
||||
|
||||
public function getAssetCountByStatuslabel()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets as assets_count')->get();
|
||||
|
||||
$statusLabels = Statuslabel::with('assets')->get();
|
||||
$labels=[];
|
||||
$points=[];
|
||||
$colors=[];
|
||||
foreach ($statusLabels as $statusLabel) {
|
||||
if ($statusLabel->assets()->count() > 0) {
|
||||
$labels[]=$statusLabel->name;
|
||||
$points[]=$statusLabel->assets()->whereNull('assigned_to')->count();
|
||||
if ($statusLabel->color!='') {
|
||||
$colors[]=$statusLabel->color;
|
||||
foreach ($statuslabels as $statuslabel) {
|
||||
if ($statuslabel->assets_count > 0) {
|
||||
|
||||
$labels[]=$statuslabel->name. ' ('.number_format($statuslabel->assets_count).')';
|
||||
$points[]=$statuslabel->assets_count;
|
||||
if ($statuslabel->color!='') {
|
||||
$colors[]=$statuslabel->color;
|
||||
}
|
||||
}
|
||||
}
|
||||
$labels[]='Deployed';
|
||||
$points[]=Asset::whereNotNull('assigned_to')->count();
|
||||
|
||||
|
||||
$colors_array = array_merge($colors, Helper::chartColors());
|
||||
|
||||
$result= [
|
||||
@@ -230,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';
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Supplier;
|
||||
use App\Http\Transformers\SuppliersTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
|
||||
class SuppliersController extends Controller
|
||||
{
|
||||
@@ -20,19 +22,24 @@ class SuppliersController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$allowed_columns = ['id','name','address','phone','contact','fax','email'];
|
||||
$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')
|
||||
)->withCount('assets')->withCount('licenses')->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);
|
||||
@@ -91,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());
|
||||
|
||||
@@ -113,10 +120,60 @@ class SuppliersController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
$supplier = Supplier::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);
|
||||
|
||||
|
||||
if ($supplier->assets_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
|
||||
}
|
||||
|
||||
if ($supplier->asset_maintenances_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count])));
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$suppliers = Supplier::select([
|
||||
'id',
|
||||
'name',
|
||||
'image',
|
||||
]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$suppliers = $suppliers->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
// 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 ($suppliers as $supplier) {
|
||||
$supplier->use_text = $supplier->name;
|
||||
$supplier->use_image = ($supplier->image) ? url('/').'/uploads/suppliers/'.$supplier->image : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($suppliers);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Helpers\Helper;
|
||||
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
|
||||
{
|
||||
@@ -25,47 +31,80 @@ 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.username',
|
||||
'users.location_id',
|
||||
'users.manager_id',
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.created_at',
|
||||
'users.notes',
|
||||
'users.activated',
|
||||
'users.address',
|
||||
'users.avatar',
|
||||
'users.city',
|
||||
'users.company_id',
|
||||
'users.last_login',
|
||||
'users.country',
|
||||
'users.created_at',
|
||||
'users.deleted_at',
|
||||
'users.department_id',
|
||||
'users.activated'
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department','throttle','assets','licenses','accessories','consumables')
|
||||
->withCount('assets','licenses','accessories','consumables');
|
||||
'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 as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count');
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
|
||||
if ($request->has('search')) {
|
||||
if (($request->filled('deleted')) && ($request->input('deleted')=='true')) {
|
||||
$users = $users->GetDeleted();
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$users = $users->where('users.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$users = $users->where('users.location_id', '=', $request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('email')) {
|
||||
$users = $users->where('users.email', '=', $request->input('email'));
|
||||
}
|
||||
|
||||
if ($request->filled('username')) {
|
||||
$users = $users->where('users.username', '=', $request->input('username'));
|
||||
}
|
||||
|
||||
if ($request->filled('group_id')) {
|
||||
$users = $users->ByGroup($request->get('group_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('department_id')) {
|
||||
$users = $users->where('users.department_id','=',$request->input('department_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
$users = $users->where('company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
$users = $users->where('location_id', '=', $request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->has('department_id')) {
|
||||
$users = $users->where('department_id','=',$request->input('department_id'));
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$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 = (($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':
|
||||
@@ -77,24 +116,91 @@ class UsersController extends Controller
|
||||
case 'department':
|
||||
$users = $users->OrderDepartment($order);
|
||||
break;
|
||||
case 'company':
|
||||
$users = $users->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns =
|
||||
[
|
||||
'last_name','first_name','email','jobtitle','username','employee_num',
|
||||
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
|
||||
'two_factor_enrolled','two_factor_optin','last_login'
|
||||
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
|
||||
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
|
||||
'country', 'zip', 'id'
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
$users = $users->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$total = $users->count();
|
||||
$users = $users->skip($offset)->take($limit)->get();
|
||||
return (new UsersTransformer)->transformUsers($users, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$users = User::select(
|
||||
[
|
||||
'users.id',
|
||||
'users.username',
|
||||
'users.employee_num',
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.gravatar',
|
||||
'users.avatar',
|
||||
'users.email',
|
||||
]
|
||||
)->where('show_in_list', '=', '1');
|
||||
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->SimpleNameSearch($request->get('search'))
|
||||
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
$users = $users->orderBy('last_name', 'asc')->orderBy('first_name', 'asc');
|
||||
$users = $users->paginate(50);
|
||||
|
||||
foreach ($users as $user) {
|
||||
$name_str = '';
|
||||
if ($user->last_name!='') {
|
||||
$name_str .= e($user->last_name).', ';
|
||||
}
|
||||
$name_str .= e($user->first_name);
|
||||
|
||||
if ($user->username!='') {
|
||||
$name_str .= ' ('.e($user->username).')';
|
||||
}
|
||||
|
||||
if ($user->employee_num!='') {
|
||||
$name_str .= ' - #'.e($user->employee_num);
|
||||
}
|
||||
|
||||
$user->use_text = $name_str;
|
||||
$user->use_image = ($user->present()->gravatar) ? $user->present()->gravatar : null;
|
||||
}
|
||||
|
||||
return (new SelectlistTransformer)->transformSelectlist($users);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
@@ -105,13 +211,33 @@ 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()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.create.success')));
|
||||
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()));
|
||||
}
|
||||
@@ -126,7 +252,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);
|
||||
}
|
||||
|
||||
@@ -142,16 +268,57 @@ 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());
|
||||
|
||||
if ($request->has('password')) {
|
||||
if ($user->id == $request->input('manager_id')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
|
||||
}
|
||||
|
||||
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')));
|
||||
}
|
||||
|
||||
@@ -173,13 +340,117 @@ 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')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of assets assigned to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function assets($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$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'));
|
||||
}
|
||||
}
|
||||
@@ -63,85 +63,6 @@ class AssetMaintenancesController extends Controller
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON response for asset maintenances listing view.
|
||||
*
|
||||
* @see AssetMaintenancesController::getIndex() method that generates view
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable(Request $request)
|
||||
{
|
||||
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company', 'admin');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$maintenances = $maintenances->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$allowed_columns = ['id','title','asset_maintenance_time','asset_maintenance_type','cost','start_date','completion_date','notes','user_id'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'user_id':
|
||||
$maintenances = $maintenances->OrderAdmin($order);
|
||||
break;
|
||||
default:
|
||||
$maintenances = $maintenances->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$maintenancesCount = $maintenances->count();
|
||||
$maintenances = $maintenances->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
foreach ($maintenances as $maintenance) {
|
||||
$actions = '';
|
||||
if (Gate::allows('update', Asset::class)) {
|
||||
$actions .= Helper::generateDatatableButton('edit', route('maintenances.edit', $maintenance->id));
|
||||
$actions .= Helper::generateDatatableButton(
|
||||
'delete',
|
||||
route('maintenances.destroy', $maintenance->id),
|
||||
$enabled = true,
|
||||
trans('admin/asset_maintenances/message.delete.confirm'),
|
||||
$maintenance->title
|
||||
);
|
||||
}
|
||||
|
||||
if (($maintenance->cost) && (isset($maintenance->asset)) && ($maintenance->asset->assetloc) && ($maintenance->asset->assetloc->currency!='')) {
|
||||
$maintenance_cost = $maintenance->asset->assetloc->currency.$maintenance->cost;
|
||||
} else {
|
||||
$maintenance_cost = $settings->default_currency.$maintenance->cost;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $maintenance->id,
|
||||
'asset_name' => ($maintenance->asset) ? (string)link_to_route('maintenances.show', $maintenance->asset->present()->Name(), ['maintenance' => $maintenance->asset->id]) : 'Deleted Asset' ,
|
||||
'title' => $maintenance->title,
|
||||
'notes' => $maintenance->notes,
|
||||
'supplier' => ($maintenance->supplier) ? (string)link_to_route('suppliers.show', $maintenance->supplier->name, ['maintenance'=>$maintenance->supplier->id]) : 'Deleted Supplier',
|
||||
'cost' => $maintenance_cost,
|
||||
'asset_maintenance_type' => e($maintenance->asset_maintenance_type),
|
||||
'start_date' => $maintenance->start_date,
|
||||
'asset_maintenance_time' => $maintenance->asset_maintenance_time,
|
||||
'completion_date' => $maintenance->completion_date,
|
||||
'user_id' => ($maintenance->admin) ? (string)link_to_route('users.show', $maintenance->admin->present()->fullName(), ['user'=>$maintenance->admin->id]) : '',
|
||||
'actions' => $actions,
|
||||
'company' => ($maintenance->asset->company) ? $maintenance->asset->company->name : ''
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $maintenancesCount, 'rows' => $rows);
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a form view to create a new asset maintenance.
|
||||
@@ -154,16 +75,21 @@ class AssetMaintenancesController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$asset = null;
|
||||
|
||||
if ($asset = Asset::find(request('asset_id'))) {
|
||||
// We have to set this so that the correct property is set in the select2 ajax dropdown
|
||||
$asset->asset_id = $asset->id;
|
||||
}
|
||||
|
||||
// Prepare Asset Maintenance Type List
|
||||
$assetMaintenanceType = [
|
||||
'' => 'Select an asset maintenance type',
|
||||
] + AssetMaintenance::getImprovementOptions();
|
||||
// Mark the selected asset, if it came in
|
||||
// Render the view
|
||||
|
||||
return view('asset_maintenances/edit')
|
||||
->with('asset_list', Helper::detailedAssetList())
|
||||
->with('selectedAsset', request('asset_id'))
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('asset', $asset)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', new AssetMaintenance);
|
||||
}
|
||||
@@ -187,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();
|
||||
}
|
||||
|
||||
@@ -236,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();
|
||||
}
|
||||
@@ -260,9 +189,7 @@ class AssetMaintenancesController extends Controller
|
||||
// Get Supplier List
|
||||
// Render the view
|
||||
return view('asset_maintenances/edit')
|
||||
->with('asset_list', Helper::detailedAssetList())
|
||||
->with('selectedAsset', null)
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', $assetMaintenance);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Models\Company;
|
||||
use Config;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
@@ -34,12 +35,12 @@ class AssetModelsController extends Controller
|
||||
* the content for the accessories listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetModelsController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', AssetModel::class);
|
||||
return view('models/index');
|
||||
}
|
||||
|
||||
@@ -52,11 +53,10 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
return view('models/edit')
|
||||
->with('category_list', Helper::categoryList('asset'))
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$category_type = 'asset';
|
||||
return view('models/edit')->with('category_type',$category_type)
|
||||
->with('depreciation_list', Helper::depreciationList())
|
||||
->with('manufacturer_list', Helper::manufacturerList())
|
||||
->with('item', new AssetModel);
|
||||
}
|
||||
|
||||
@@ -68,9 +68,10 @@ class AssetModelsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Create a new asset model
|
||||
$model = new AssetModel;
|
||||
|
||||
@@ -82,71 +83,27 @@ class AssetModelsController extends Controller
|
||||
$model->manufacturer_id = $request->input('manufacturer_id');
|
||||
$model->category_id = $request->input('category_id');
|
||||
$model->notes = $request->input('notes');
|
||||
$model->user_id = Auth::guard('api')->user();
|
||||
$model->user_id = Auth::id();
|
||||
$model->requestable = Input::has('requestable');
|
||||
|
||||
if ($request->input('custom_fieldset')!='') {
|
||||
$model->fieldset_id = e($request->input('custom_fieldset'));
|
||||
}
|
||||
|
||||
if (Input::file('image')) {
|
||||
$image = Input::file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/models/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$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
|
||||
$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.
|
||||
*
|
||||
@@ -157,17 +114,16 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function edit($modelId = null)
|
||||
{
|
||||
// Check if the model exists
|
||||
if (is_null($item = AssetModel::find($modelId))) {
|
||||
// Redirect to the model management page
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
$this->authorize('update', AssetModel::class);
|
||||
if ($item = AssetModel::find($modelId)) {
|
||||
$category_type = 'asset';
|
||||
$view = View::make('models/edit', compact('item','category_type'));
|
||||
$view->with('depreciation_list', Helper::depreciationList());
|
||||
return $view;
|
||||
}
|
||||
|
||||
$view = View::make('models/edit', compact('item'));
|
||||
$view->with('category_list', Helper::categoryList('asset'));
|
||||
$view->with('depreciation_list', Helper::depreciationList());
|
||||
$view->with('manufacturer_list', Helper::manufacturerList());
|
||||
return $view;
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -180,44 +136,37 @@ class AssetModelsController extends Controller
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function update(Request $request, $modelId = null)
|
||||
public function update(ImageUploadRequest $request, $modelId = null)
|
||||
{
|
||||
$this->authorize('update', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model = AssetModel::find($modelId))) {
|
||||
// Redirect to the models management page
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$model->depreciation_id = $request->input('depreciation_id');
|
||||
$model->eol = $request->input('eol');
|
||||
$model->depreciation_id = $request->input('depreciation_id');
|
||||
$model->eol = $request->input('eol');
|
||||
$model->name = $request->input('name');
|
||||
$model->model_number = $request->input('model_number');
|
||||
$model->manufacturer_id = $request->input('manufacturer_id');
|
||||
$model->category_id = $request->input('category_id');
|
||||
$model->notes = $request->input('notes');
|
||||
$model->requestable = $request->input('requestable', '0');
|
||||
|
||||
$model->requestable = Input::has('requestable');
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if ($request->input('custom_fieldset')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = $request->input('custom_fieldset');
|
||||
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'));
|
||||
}
|
||||
}
|
||||
|
||||
if (Input::file('image')) {
|
||||
$image = Input::file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/models/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(300, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$model->image = $file_name;
|
||||
}
|
||||
|
||||
if ($request->input('image_delete') == 1 && Input::file('image') == "") {
|
||||
$model->image = null;
|
||||
}
|
||||
$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'));
|
||||
@@ -236,6 +185,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function destroy($modelId)
|
||||
{
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model = AssetModel::find($modelId))) {
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found'));
|
||||
@@ -245,6 +195,15 @@ class AssetModelsController extends Controller
|
||||
// Throw an error that this model is associated with assets
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.assoc_users'));
|
||||
}
|
||||
|
||||
if ($model->image) {
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$model->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the model
|
||||
$model->delete();
|
||||
|
||||
@@ -263,7 +222,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function getRestore($modelId = null)
|
||||
{
|
||||
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Get user information
|
||||
$model = AssetModel::withTrashed()->find($modelId);
|
||||
|
||||
@@ -294,16 +253,14 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function show($modelId = null)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$model = AssetModel::withTrashed()->find($modelId);
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,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;
|
||||
@@ -352,49 +307,6 @@ class AssetModelsController extends Controller
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the asset information to present to the model view detail page
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @param Request $request
|
||||
* @param $modelID
|
||||
* @return String JSON
|
||||
* @internal param int $modelId
|
||||
*/
|
||||
public function getDataView(Request $request, $modelID)
|
||||
{
|
||||
$assets = Asset::where('model_id', '=', $modelID)->with('company', 'assetstatus');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$assets = $assets->TextSearch(e($request->input('search')));
|
||||
}
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
|
||||
$allowed_columns = ['name', 'serial','asset_tag'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
$assets = $assets->orderBy($sort, $order);
|
||||
|
||||
$assetsCount = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
$all_custom_fields = CustomField::all();
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$rows[] = $asset->present()->forDataTable($all_custom_fields);
|
||||
}
|
||||
|
||||
$data = array('total' => $assetsCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that allows the user to bulk edit model attrbutes
|
||||
@@ -405,20 +317,41 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
$models_raw_array = Input::get('ids');
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->get();
|
||||
$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);
|
||||
$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 as assets_count')->orderBy('assets_count', 'ASC')->get();
|
||||
|
||||
// If deleting....
|
||||
if ($request->input('bulk_actions')=='delete') {
|
||||
$valid_count = 0;
|
||||
foreach ($models as $model) {
|
||||
if ($model->assets_count == 0) {
|
||||
$valid_count++;
|
||||
}
|
||||
}
|
||||
return view('models/bulk-delete', compact('models'))->with('valid_count', $valid_count);
|
||||
|
||||
// Otherwise display the bulk edit screen
|
||||
} else {
|
||||
|
||||
$nochange = ['NC' => 'No Change'];
|
||||
$fieldset_list = $nochange + Helper::customFieldsetList();
|
||||
$depreciation_list = $nochange + Helper::depreciationList();
|
||||
|
||||
return view('models/bulk-edit', compact('models'))
|
||||
->with('fieldset_list', $fieldset_list)
|
||||
->with('depreciation_list', $depreciation_list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('models.index')
|
||||
->with('error', 'You must select at least one model to edit.');
|
||||
|
||||
}
|
||||
|
||||
@@ -437,10 +370,11 @@ class AssetModelsController extends Controller
|
||||
$models_raw_array = Input::get('ids');
|
||||
$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') {
|
||||
@@ -463,4 +397,91 @@ class AssetModelsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and delete the given Asset Models. An Asset Model
|
||||
* cannot be deleted if there are associated assets.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postBulkDelete(Request $request)
|
||||
{
|
||||
$models_raw_array = Input::get('ids');
|
||||
|
||||
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
|
||||
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->get();
|
||||
|
||||
$del_error_count = 0;
|
||||
$del_count = 0;
|
||||
|
||||
foreach ($models as $model) {
|
||||
\Log::debug($model->id);
|
||||
|
||||
if ($model->assets_count > 0) {
|
||||
$del_error_count++;
|
||||
} else {
|
||||
$model->delete();
|
||||
$del_count++;
|
||||
}
|
||||
}
|
||||
|
||||
\Log::debug($del_count);
|
||||
\Log::debug($del_error_count);
|
||||
|
||||
if ($del_error_count == 0) {
|
||||
return redirect()->route('models.index')
|
||||
->with('success', trans('admin/models/message.bulkdelete.success',['success_count'=> $del_count] ));
|
||||
}
|
||||
|
||||
return redirect()->route('models.index')
|
||||
->with('warning', trans('admin/models/message.bulkdelete.success_partial', ['fail_count'=>$del_error_count, 'success_count'=> $del_count]));
|
||||
}
|
||||
|
||||
return redirect()->route('models.index')
|
||||
->with('error', trans('admin/models/message.bulkdelete.error'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)]
|
||||
);
|
||||
|
||||
@@ -47,49 +47,75 @@ class LoginController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
|
||||
\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);
|
||||
|
||||
@@ -113,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()) {
|
||||
@@ -131,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 {
|
||||
|
||||
@@ -163,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
|
||||
@@ -179,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);
|
||||
|
||||
}
|
||||
|
||||
@@ -210,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');
|
||||
}
|
||||
|
||||
@@ -222,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'));
|
||||
|
||||
|
||||
}
|
||||
@@ -250,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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -277,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(
|
||||
@@ -292,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');
|
||||
@@ -320,4 +381,9 @@ class LoginController extends Controller
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
public function redirectTo()
|
||||
{
|
||||
return Session::get('backUrl') ? Session::get('backUrl') : $this->redirectTo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
23
app/Http/Controllers/Auth/RegisterController.php
Normal file
23
app/Http/Controllers/Auth/RegisterController.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
public function showRegistrationForm() {
|
||||
abort(404,'Page not found');
|
||||
}
|
||||
|
||||
public function register() {
|
||||
abort(404,'Page not found');
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -25,7 +27,7 @@ class ResetPasswordController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/home';
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ use Lang;
|
||||
use Redirect;
|
||||
use Str;
|
||||
use View;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to Categories for
|
||||
@@ -38,6 +40,7 @@ class CategoriesController extends Controller
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
$this->authorize('view', Category::class);
|
||||
return view('categories/index');
|
||||
}
|
||||
|
||||
@@ -53,6 +56,7 @@ class CategoriesController extends Controller
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$this->authorize('create', Category::class);
|
||||
$category_types= Helper::categoryTypeList();
|
||||
return view('categories/edit')->with('item', new Category)
|
||||
->with('category_types', $category_types);
|
||||
@@ -67,11 +71,10 @@ class CategoriesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
// create a new model instance
|
||||
$this->authorize('create', Category::class);
|
||||
$category = new Category();
|
||||
// Update the category data
|
||||
$category->name = $request->input('name');
|
||||
$category->category_type = $request->input('category_type');
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
@@ -80,6 +83,8 @@ class CategoriesController extends Controller
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->user_id = Auth::id();
|
||||
|
||||
$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'));
|
||||
}
|
||||
@@ -98,10 +103,9 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function edit($categoryId = null)
|
||||
{
|
||||
// Check if the category exists
|
||||
$this->authorize('edit', Category::class);
|
||||
if (is_null($item = Category::find($categoryId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.does_not_exist'));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.does_not_exist'));
|
||||
}
|
||||
$category_types= Helper::categoryTypeList();
|
||||
|
||||
@@ -120,9 +124,9 @@ class CategoriesController extends Controller
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function update(Request $request, $categoryId = null)
|
||||
public function update(ImageUploadRequest $request, $categoryId = null)
|
||||
{
|
||||
// Check if the blog post exists
|
||||
$this->authorize('edit', Category::class);
|
||||
if (is_null($category = Category::find($categoryId))) {
|
||||
// Redirect to the categories management page
|
||||
return redirect()->to('admin/categories')->with('error', trans('admin/categories/message.does_not_exist'));
|
||||
@@ -138,6 +142,13 @@ class CategoriesController extends Controller
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$category->image = null;
|
||||
}
|
||||
|
||||
$category = $request->handleImages($category,600, public_path().'/uploads/categories');
|
||||
|
||||
if ($category->save()) {
|
||||
// Redirect to the new category page
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.update.success'));
|
||||
@@ -156,24 +167,25 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function destroy($categoryId)
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::find($categoryId))) {
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.not_found'));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
|
||||
}
|
||||
|
||||
if ($category->has_models() > 0) {
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'model']));
|
||||
} elseif ($category->accessories()->count() > 0) {
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory']));
|
||||
} elseif ($category->consumables()->count() > 0) {
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable']));
|
||||
} elseif ($category->components()->count() > 0) {
|
||||
return redirect()->to('admin/settings/categories')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=>'component']));
|
||||
}
|
||||
|
||||
$category->delete();
|
||||
// Redirect to the locations management page
|
||||
return redirect()->to(route('categories.index'))->with('success', trans('admin/categories/message.delete.success'));
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +201,7 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
if ($category = Category::find($id)) {
|
||||
|
||||
if ($category->category_type=='asset') {
|
||||
@@ -206,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;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use Lang;
|
||||
use Redirect;
|
||||
use View;
|
||||
use Illuminate\Http\Request;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Companies for
|
||||
@@ -27,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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +43,8 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
return view('companies/edit')->with('item', new Company);
|
||||
}
|
||||
|
||||
@@ -50,11 +56,15 @@ final class CompaniesController extends Controller
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
$company = new Company;
|
||||
$company->name = $request->input('name');
|
||||
|
||||
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('success', trans('admin/companies/message.create.success'));
|
||||
@@ -77,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);
|
||||
}
|
||||
|
||||
@@ -89,14 +102,24 @@ final class CompaniesController extends Controller
|
||||
* @param int $companyId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, $companyId)
|
||||
public function update(ImageUploadRequest $request, $companyId)
|
||||
{
|
||||
if (is_null($company = Company::find($companyId))) {
|
||||
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $company);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$company = $request->handleImages($company,600, public_path().'/uploads/companies');
|
||||
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('success', trans('admin/companies/message.update.success'));
|
||||
@@ -119,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')
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\CustomField;
|
||||
@@ -21,6 +23,7 @@ use View;
|
||||
use Validator;
|
||||
use Illuminate\Http\Request;
|
||||
use Gate;
|
||||
use Image;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to Components for
|
||||
@@ -57,12 +60,9 @@ class ComponentsController extends Controller
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Component::class);
|
||||
// Show the page
|
||||
return view('components/edit')
|
||||
->with('item', new Component)
|
||||
->with('category_list', Helper::categoryList('component'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList());
|
||||
$category_type = 'component';
|
||||
return view('components/edit')->with('category_type',$category_type)
|
||||
->with('item', new Component);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,22 +74,25 @@ class ComponentsController extends Controller
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store()
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Component::class);
|
||||
$component = new Component();
|
||||
$component->name = Input::get('name');
|
||||
$component->category_id = Input::get('category_id');
|
||||
$component->location_id = Input::get('location_id');
|
||||
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$component->order_number = Input::get('order_number');
|
||||
$component->min_amt = Input::get('min_amt');
|
||||
$component->serial = Input::get('serial');
|
||||
$component->purchase_date = Input::get('purchase_date');
|
||||
$component->purchase_cost = request('purchase_cost');
|
||||
$component->qty = Input::get('qty');
|
||||
$component->name = $request->input('name');
|
||||
$component->category_id = $request->input('category_id');
|
||||
$component->location_id = $request->input('location_id');
|
||||
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
||||
$component->order_number = $request->input('order_number', null);
|
||||
$component->min_amt = $request->input('min_amt', null);
|
||||
$component->serial = $request->input('serial', null);
|
||||
$component->purchase_date = $request->input('purchase_date', null);
|
||||
$component->purchase_cost = $request->input('purchase_cost', null);
|
||||
$component->qty = $request->input('qty');
|
||||
$component->user_id = Auth::id();
|
||||
|
||||
|
||||
$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'));
|
||||
}
|
||||
@@ -107,16 +110,18 @@ class ComponentsController extends Controller
|
||||
*/
|
||||
public function edit($componentId = null)
|
||||
{
|
||||
if (is_null($item = Component::find($componentId))) {
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
|
||||
|
||||
if ($item = Component::find($componentId)) {
|
||||
$this->authorize('update', $item);
|
||||
$category_type = 'component';
|
||||
return view('components/edit', compact('item'))->with('category_type', $category_type);
|
||||
}
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
|
||||
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('components/edit', compact('item'))
|
||||
->with('category_list', Helper::categoryList('component'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList());
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +134,7 @@ class ComponentsController extends Controller
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update($componentId = null)
|
||||
public function update(ImageUploadRequest $request, $componentId = null)
|
||||
{
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
@@ -150,6 +155,8 @@ class ComponentsController extends Controller
|
||||
$component->purchase_cost = request('purchase_cost');
|
||||
$component->qty = Input::get('qty');
|
||||
|
||||
$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'));
|
||||
}
|
||||
@@ -167,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);
|
||||
@@ -175,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.
|
||||
*
|
||||
@@ -205,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'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +220,7 @@ class ComponentsController extends Controller
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
$this->authorize('checkout', $component);
|
||||
return view('components/checkout', compact('component'))->with('assets_list', Helper::detailedAssetList());
|
||||
return view('components/checkout', compact('component'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,40 +275,101 @@ class ComponentsController extends Controller
|
||||
'asset_id' => $asset_id
|
||||
]);
|
||||
|
||||
$component->logCheckout(e(Input::get('note')), $asset_id);
|
||||
$component->logCheckout(e(Input::get('note')), $asset);
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that allows the checkin of a component from an asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::postCheckout() method that stores the data.
|
||||
* @since [v4.1.4]
|
||||
* @param int $componentId
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getCheckin($component_asset_id)
|
||||
{
|
||||
|
||||
// This could probably be done more cleanly but I am very tired. - @snipe
|
||||
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
|
||||
if (is_null($component = Component::find($component_assets->component_id))) {
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
|
||||
}
|
||||
if (is_null($asset = Asset::find($component_assets->asset_id))) {
|
||||
return redirect()->route('components.index')->with('error',
|
||||
trans('admin/components/message.not_found'));
|
||||
}
|
||||
$this->authorize('checkin', $component);
|
||||
return view('components/checkin', compact('component_assets','component','asset'));
|
||||
}
|
||||
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/messages.not_found'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON data to populate the components view,
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getView() method that returns the view.
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function getDataView($componentId)
|
||||
* Validate and store checkin data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getCheckout() method that returns the form.
|
||||
* @since [v4.1.4]
|
||||
* @param Request $request
|
||||
* @param int $componentId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postCheckin(Request $request, $component_asset_id)
|
||||
{
|
||||
if (is_null($component = Component::with('assets')->find($componentId))) {
|
||||
// Redirect to the component management page with error
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
|
||||
if (is_null($component = Component::find($component_assets->component_id))) {
|
||||
return redirect()->route('components.index')->with('error',
|
||||
trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($component)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
$this->authorize('view', $component);
|
||||
|
||||
$rows = array();
|
||||
$all_custom_fields = CustomField::all(); // Cached for table;
|
||||
foreach ($component->assets as $component_assignment) {
|
||||
$rows[] = $component_assignment->present()->forDataTable($all_custom_fields);
|
||||
}
|
||||
$this->authorize('checkin', $component);
|
||||
|
||||
$componentCount = $component->assets->count();
|
||||
$data = array('total' => $componentCount, 'rows' => $rows);
|
||||
return $data;
|
||||
$max_to_checkin = $component_assets->assigned_qty;
|
||||
$validator = Validator::make($request->all(), [
|
||||
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
|
||||
// Validation passed, so let's figure out what we have to do here.
|
||||
$qty_remaining_in_checkout = ($component_assets->assigned_qty - (int)$request->input('checkin_qty'));
|
||||
|
||||
// We have to modify the record to reflect the new qty that's
|
||||
// actually checked out.
|
||||
$component_assets->assigned_qty = $qty_remaining_in_checkout;
|
||||
DB::table('components_assets')->where('id',
|
||||
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
|
||||
|
||||
$log = new Actionlog();
|
||||
$log->user_id = Auth::user()->id;
|
||||
$log->action_type = 'checkin from';
|
||||
$log->target_type = Asset::class;
|
||||
$log->target_id = $component_assets->asset_id;
|
||||
$log->item_id = $component_assets->component_id;
|
||||
$log->item_type = Component::class;
|
||||
$log->note = $request->input('note');
|
||||
$log->save();
|
||||
|
||||
// If the checked-in qty is exactly the same as the assigned_qty,
|
||||
// we can simply delete the associated components_assets record
|
||||
if ($qty_remaining_in_checkout == 0) {
|
||||
DB::table('components_assets')->where('id', '=', $component_asset_id)->delete();
|
||||
}
|
||||
|
||||
return redirect()->route('components.index')->with('success',
|
||||
trans('admin/components/message.checkout.success'));
|
||||
}
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ 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;
|
||||
use View;
|
||||
use Gate;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Consumables for
|
||||
@@ -54,13 +54,9 @@ class ConsumablesController extends Controller
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
// Show the page
|
||||
return view('consumables/edit')
|
||||
->with('item', new Consumable)
|
||||
->with('category_list', Helper::categoryList('consumable'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
$category_type = 'consumable';
|
||||
return view('consumables/edit')->with('category_type', $category_type)
|
||||
->with('item', new Consumable);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,24 +68,28 @@ class ConsumablesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store()
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable();
|
||||
$consumable->name = Input::get('name');
|
||||
$consumable->category_id = Input::get('category_id');
|
||||
$consumable->location_id = Input::get('location_id');
|
||||
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$consumable->order_number = Input::get('order_number');
|
||||
$consumable->min_amt = Input::get('min_amt');
|
||||
$consumable->manufacturer_id = Input::get('manufacturer_id');
|
||||
$consumable->model_number = Input::get('model_number');
|
||||
$consumable->item_no = Input::get('item_no');
|
||||
$consumable->purchase_date = Input::get('purchase_date');
|
||||
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
|
||||
$consumable->qty = Input::get('qty');
|
||||
$consumable->name = $request->input('name');
|
||||
$consumable->category_id = $request->input('category_id');
|
||||
$consumable->location_id = $request->input('location_id');
|
||||
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
||||
$consumable->order_number = $request->input('order_number');
|
||||
$consumable->min_amt = $request->input('min_amt');
|
||||
$consumable->manufacturer_id = $request->input('manufacturer_id');
|
||||
$consumable->model_number = $request->input('model_number');
|
||||
$consumable->item_no = $request->input('item_no');
|
||||
$consumable->purchase_date = $request->input('purchase_date');
|
||||
$consumable->purchase_cost = Helper::ParseFloat($request->input('purchase_cost'));
|
||||
$consumable->qty = $request->input('qty');
|
||||
$consumable->user_id = Auth::id();
|
||||
|
||||
|
||||
$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'));
|
||||
}
|
||||
@@ -109,17 +109,14 @@ class ConsumablesController extends Controller
|
||||
*/
|
||||
public function edit($consumableId = null)
|
||||
{
|
||||
if (is_null($item = Consumable::find($consumableId))) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
if ($item = Consumable::find($consumableId)) {
|
||||
$this->authorize($item);
|
||||
$category_type = 'consumable';
|
||||
return view('consumables/edit', compact('item'))->with('category_type', $category_type);
|
||||
}
|
||||
|
||||
$this->authorize($item);
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
|
||||
return view('consumables/edit', compact('item'))
|
||||
->with('category_list', Helper::categoryList('consumable'))
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +129,7 @@ class ConsumablesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update($consumableId = null)
|
||||
public function update(ImageUploadRequest $request, $consumableId = null)
|
||||
{
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
@@ -140,19 +137,32 @@ class ConsumablesController extends Controller
|
||||
|
||||
$this->authorize($consumable);
|
||||
|
||||
$consumable->name = Input::get('name');
|
||||
$consumable->category_id = Input::get('category_id');
|
||||
$consumable->location_id = Input::get('location_id');
|
||||
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$consumable->order_number = Input::get('order_number');
|
||||
$consumable->min_amt = Input::get('min_amt');
|
||||
$consumable->manufacturer_id = Input::get('manufacturer_id');
|
||||
$consumable->model_number = Input::get('model_number');
|
||||
$consumable->item_no = Input::get('item_no');
|
||||
$consumable->purchase_date = Input::get('purchase_date');
|
||||
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
|
||||
$consumable->name = $request->input('name');
|
||||
$consumable->category_id = $request->input('category_id');
|
||||
$consumable->location_id = $request->input('location_id');
|
||||
$consumable->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
||||
$consumable->order_number = $request->input('order_number');
|
||||
$consumable->min_amt = $request->input('min_amt');
|
||||
$consumable->manufacturer_id = $request->input('manufacturer_id');
|
||||
$consumable->model_number = $request->input('model_number');
|
||||
$consumable->item_no = $request->input('item_no');
|
||||
$consumable->purchase_date = $request->input('purchase_date');
|
||||
$consumable->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
|
||||
$consumable->qty = Helper::ParseFloat(Input::get('qty'));
|
||||
|
||||
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(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$consumable->image = $file_name;
|
||||
} elseif ($request->input('image_delete')=='1') {
|
||||
$consumable->image = null;
|
||||
}
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
|
||||
}
|
||||
@@ -194,7 +204,7 @@ class ConsumablesController extends Controller
|
||||
if (isset($consumable->id)) {
|
||||
return view('consumables/view', compact('consumable'));
|
||||
}
|
||||
return redirect()->route('consumables')->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'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,10 +219,10 @@ class ConsumablesController extends Controller
|
||||
public function getCheckout($consumableId)
|
||||
{
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
}
|
||||
$this->authorize('checkout', $consumable);
|
||||
return view('consumables/checkout', compact('consumable'))->with('users_list', Helper::usersList());
|
||||
return view('consumables/checkout', compact('consumable'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +248,7 @@ class ConsumablesController extends Controller
|
||||
// Check if the user exists
|
||||
if (is_null($user = User::find($assigned_to))) {
|
||||
// Redirect to the consumable management page with error
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.user_does_not_exist'));
|
||||
return redirect()->route('checkout/consumable', $consumable)->with('error', trans('admin/consumables/message.checkout.user_does_not_exist'));
|
||||
}
|
||||
|
||||
// Update the consumable data
|
||||
@@ -250,7 +260,7 @@ class ConsumablesController extends Controller
|
||||
'assigned_to' => e(Input::get('assigned_to'))
|
||||
]);
|
||||
|
||||
$logaction = $consumable->logCheckout(e(Input::get('note')));
|
||||
$logaction = $consumable->logCheckout(e(Input::get('note')), $user);
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['eula'] = $consumable->getEula();
|
||||
$data['first_name'] = $user->first_name;
|
||||
@@ -259,61 +269,10 @@ class ConsumablesController extends Controller
|
||||
$data['note'] = $logaction->note;
|
||||
$data['require_acceptance'] = $consumable->requireAcceptance();
|
||||
|
||||
if (($consumable->requireAcceptance()=='1') || ($consumable->getEula())) {
|
||||
|
||||
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'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON response containing details on the users associated with this consumable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::getView() method that returns the form.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return array
|
||||
*/
|
||||
public function getDataView($consumableId)
|
||||
{
|
||||
//$consumable = Consumable::find($consumableID);
|
||||
$consumable = Consumable::with(array('consumableAssigments'=>
|
||||
function ($query) {
|
||||
$query->orderBy('created_at', 'DESC');
|
||||
},
|
||||
'consumableAssigments.admin'=> function ($query) {
|
||||
},
|
||||
'consumableAssigments.user'=> function ($query) {
|
||||
},
|
||||
))->find($consumableId);
|
||||
|
||||
// $consumable->load('consumableAssigments.admin','consumableAssigments.user');
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return ['total' => 0, 'rows' => []];
|
||||
}
|
||||
$this->authorize('view', Component::class);
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumable->consumableAssigments as $consumable_assignment) {
|
||||
$rows[] = [
|
||||
'name' => $consumable_assignment->user->present()->nameUrl(),
|
||||
'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'),
|
||||
'admin' => ($consumable_assignment->admin) ? $consumable_assignment->admin->present()->nameUrl() : '',
|
||||
];
|
||||
}
|
||||
|
||||
$consumableCount = $consumable->users->count();
|
||||
$data = array('total' => $consumableCount, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\CustomFieldRequest;
|
||||
use View;
|
||||
use App\Models\CustomFieldset;
|
||||
use App\Models\CustomField;
|
||||
@@ -36,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();
|
||||
@@ -56,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());
|
||||
}
|
||||
@@ -69,39 +72,33 @@ class CustomFieldsController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(Request $request)
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
$validator = Validator::make(Input::all(), $field->rules);
|
||||
|
||||
if ($validator->passes()) {
|
||||
$results = $field->save();
|
||||
if ($results) {
|
||||
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'));
|
||||
}
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
|
||||
} else {
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -116,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'));
|
||||
}
|
||||
@@ -134,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 {
|
||||
@@ -155,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);
|
||||
}
|
||||
|
||||
@@ -169,16 +173,18 @@ class CustomFieldsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(CustomFieldRequest $request, $id)
|
||||
{
|
||||
$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"));
|
||||
@@ -186,13 +192,11 @@ class CustomFieldsController extends Controller
|
||||
$field->format = e($request->get("format"));
|
||||
}
|
||||
|
||||
$validator = Validator::make(Input::all(), $field->rules);
|
||||
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,19 +37,27 @@ class CustomFieldsetsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
|
||||
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
|
||||
|
||||
$maxid = 0;
|
||||
foreach ($cfset->fields() as $field) {
|
||||
if ($field->pivot->order > $maxid) {
|
||||
$maxid=$field->pivot->order;
|
||||
}
|
||||
if (isset($custom_fields_list[$field->id])) {
|
||||
unset($custom_fields_list[$field->id]);
|
||||
$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) {
|
||||
if ($field->pivot->order > $maxid) {
|
||||
$maxid=$field->pivot->order;
|
||||
}
|
||||
if (isset($custom_fields_list[$field->id])) {
|
||||
unset($custom_fields_list[$field->id]);
|
||||
}
|
||||
}
|
||||
|
||||
return view("custom_fields.fieldsets.view")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
|
||||
}
|
||||
|
||||
return view("custom_fields.fieldsets.view")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
|
||||
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +70,8 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
return view("custom_fields.fieldsets.edit");
|
||||
}
|
||||
|
||||
@@ -75,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")),
|
||||
@@ -133,16 +145,23 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$models = AssetModel::where("fieldset_id", "=", $id);
|
||||
if ($models->count() == 0) {
|
||||
$fieldset->delete();
|
||||
return redirect()->route("fields.show")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
|
||||
} else {
|
||||
return redirect()->route("fields.show")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
|
||||
$this->authorize('delete', $fieldset);
|
||||
|
||||
if ($fieldset) {
|
||||
$models = AssetModel::where("fieldset_id", "=", $id);
|
||||
if ($models->count() == 0) {
|
||||
$fieldset->delete();
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
|
||||
} else {
|
||||
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -153,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'));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class DashboardController extends Controller
|
||||
$counts['grand_total'] = $counts['asset'] + $counts['accessory'] + $counts['license'] + $counts['consumable'];
|
||||
|
||||
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
|
||||
\Artisan::call('passport:install');
|
||||
\Artisan::call('migrate', ['--force' => true]);
|
||||
\Artisan::call('passport:install');
|
||||
}
|
||||
|
||||
return view('dashboard')->with('asset_stats', $asset_stats)->with('counts', $counts);
|
||||
|
||||
@@ -6,6 +6,8 @@ use Illuminate\Http\Request;
|
||||
use App\Models\Department;
|
||||
use App\Helpers\Helper;
|
||||
use Auth;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
class DepartmentsController extends Controller
|
||||
{
|
||||
@@ -27,13 +29,11 @@ class DepartmentsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('index', Department::class);
|
||||
if ($request->has('company_id')) {
|
||||
$company = null;
|
||||
if ($request->filled('company_id')) {
|
||||
$company = Company::find($request->input('company_id'));
|
||||
} else {
|
||||
$company = null;
|
||||
}
|
||||
return view('departments/index')->with('company',$company);
|
||||
|
||||
return view('departments/index')->with('company', $company);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,20 +45,20 @@ class DepartmentsController extends Controller
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$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);
|
||||
|
||||
$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'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($department->getErrors());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -91,10 +93,9 @@ class DepartmentsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('departments/edit')->with('item', new Department)
|
||||
->with('manager_list', Helper::managerList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('company_list', Helper::companyList());
|
||||
$this->authorize('create', Department::class);
|
||||
|
||||
return view('departments/edit')->with('item', new Department);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,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'));
|
||||
}
|
||||
@@ -135,30 +138,28 @@ class DepartmentsController extends Controller
|
||||
if (is_null($item = Department::find($id))) {
|
||||
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
return view('departments/edit', compact('item'))
|
||||
->with('manager_list', Helper::managerList())
|
||||
->with('location_list', Helper::locationsList())
|
||||
->with('company_list', Helper::companyList());
|
||||
}
|
||||
|
||||
|
||||
public function update(Request $request, $id) {
|
||||
|
||||
$this->authorize('create', Department::class);
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('departments/edit', compact('item'));
|
||||
}
|
||||
|
||||
public function update(ImageUploadRequest $request, $id) {
|
||||
|
||||
if (is_null($department = Department::find($id))) {
|
||||
return redirect()->to('admin/settings/departments')->with('error', trans('admin/departments/message.does_not_exist'));
|
||||
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);
|
||||
|
||||
$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'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($department->getErrors());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
@@ -155,5 +167,26 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('success', trans('admin/depreciations/message.delete.success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays a form to display depreciation listing
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::postEdit()
|
||||
* @param int $depreciationId
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
if (is_null($depreciation = Depreciation::find($id))) {
|
||||
// Redirect to the blogs management page
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ class GroupsController extends Controller
|
||||
$group = new Group;
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = array();
|
||||
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
|
||||
$selectedPermissions = Input::old('permissions', $groupPermissions);
|
||||
|
||||
// Show the page
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,13 +84,18 @@ class GroupsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($id = null)
|
||||
public function edit($id)
|
||||
{
|
||||
$group = Group::find($id);
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = $group->decodePermissions();
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
|
||||
|
||||
if ($group) {
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = $group->decodePermissions();
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
|
||||
}
|
||||
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,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'));
|
||||
@@ -133,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
|
||||
@@ -142,4 +147,24 @@ class GroupsController extends Controller
|
||||
return redirect()->route('groups.index')->with('error', trans('general.feature_disabled'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the group detail page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $locationId
|
||||
* @since [v4.0.11]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$group = Group::find($id);
|
||||
|
||||
if ($group) {
|
||||
return view('groups/view', compact('group'));
|
||||
}
|
||||
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -67,10 +68,7 @@ class LicensesController extends Controller
|
||||
return view('licenses/edit')
|
||||
//->with('license_options',$license_options)
|
||||
->with('depreciation_list', Helper::depreciationList())
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('maintained_list', $maintained_list)
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('manufacturer_list', Helper::manufacturerList())
|
||||
->with('item', new License);
|
||||
|
||||
}
|
||||
@@ -110,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();
|
||||
|
||||
@@ -144,10 +143,7 @@ class LicensesController extends Controller
|
||||
|
||||
return view('licenses/edit', compact('item'))
|
||||
->with('depreciation_list', Helper::depreciationList())
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('maintained_list', $maintained_list)
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
->with('maintained_list', $maintained_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -183,10 +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'));
|
||||
@@ -244,18 +244,25 @@ class LicensesController extends Controller
|
||||
* @param int $seatId
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getCheckout($seatId)
|
||||
public function getCheckout($licenceId)
|
||||
{
|
||||
// Check if the license seat exists
|
||||
if (is_null($licenseSeat = LicenseSeat::find($seatId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
|
||||
// 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', $licenseSeat);
|
||||
return view('licenses/checkout', compact('licenseSeat'))
|
||||
->with('users_list', Helper::usersList())
|
||||
->with('asset_list', Helper::detailedAssetList());
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -266,83 +273,96 @@ 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, $seatId)
|
||||
public function postCheckout(Request $request, $licenseId, $seatId = null)
|
||||
{
|
||||
$licenseSeat = LicenseSeat::find($seatId);
|
||||
$assigned_to = e($request->input('assigned_to'));
|
||||
$asset_id = e($request->input('asset_id'));
|
||||
|
||||
$this->authorize('checkout', $licenseSeat);
|
||||
|
||||
// Declare the rules for the form validation
|
||||
$rules = [
|
||||
'note' => 'string',
|
||||
'asset_id' => 'required_without:assigned_to',
|
||||
];
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if ($assigned_to!='') {
|
||||
// Check if the user exists
|
||||
if (is_null($is_assigned_to = User::find($assigned_to))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
|
||||
// Check that the license is valid
|
||||
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');
|
||||
}
|
||||
}
|
||||
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 ($asset_id!='') {
|
||||
if (is_null($asset = Asset::find($asset_id))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
|
||||
|
||||
|
||||
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
|
||||
// Declare the rules for the form validation
|
||||
$rules = [
|
||||
'note' => 'string|nullable',
|
||||
'asset_id' => 'required_without:assigned_to',
|
||||
];
|
||||
|
||||
// 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);
|
||||
}
|
||||
$target = null;
|
||||
|
||||
|
||||
// This item is checked out to a an asset
|
||||
if (request('checkout_to_type')=='asset') {
|
||||
if (is_null($target = Asset::find(request('asset_id')))) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
|
||||
}
|
||||
$licenseSeat->asset_id = $request->input('asset_id');
|
||||
|
||||
// Override asset's assigned user if available
|
||||
if ($target->checkedOutToUser()) {
|
||||
$licenseSeat->assigned_to = $target->assigned_to;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Fetch the target and set the license user
|
||||
if (is_null($target = User::find(request('assigned_to')))) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
|
||||
}
|
||||
$licenseSeat->assigned_to = request('assigned_to');
|
||||
}
|
||||
|
||||
if (($asset->assigned_to!='') && (($asset->assigned_to!=$assigned_to)) && ($assigned_to!='')) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.owner_doesnt_match_asset'));
|
||||
$licenseSeat->user_id = Auth::user()->id;
|
||||
|
||||
|
||||
if ($licenseSeat->save()) {
|
||||
$licenseSeat->logCheckout($request->input('note'), $target);
|
||||
return redirect()->route("licenses.index")->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
}
|
||||
|
||||
}
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
|
||||
// Check if the asset exists
|
||||
if (is_null($licenseSeat)) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
}
|
||||
|
||||
if ($request->input('asset_id') == '') {
|
||||
$licenseSeat->asset_id = null;
|
||||
} else {
|
||||
$licenseSeat->asset_id = $request->input('asset_id');
|
||||
}
|
||||
|
||||
// Update the asset data
|
||||
if ($request->input('assigned_to') == '') {
|
||||
$licenseSeat->assigned_to = null;
|
||||
} else {
|
||||
$licenseSeat->assigned_to = $request->input('assigned_to');
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
$licenseSeat->logCheckout($request->input('note'));
|
||||
|
||||
$data['license_id'] =$licenseSeat->license_id;
|
||||
$data['note'] = $request->input('note');
|
||||
|
||||
// Redirect to the new asset page
|
||||
return redirect()->route("licenses.index")->with('success', trans('admin/licenses/message.checkout.success'));
|
||||
}
|
||||
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("admin/licenses/{$asset_id}/checkout")->with('error', trans('admin/licenses/message.create.error'))->with('license', new License);
|
||||
return redirect()->route("licenses.index")->with('error', trans('admin/licenses/message.checkout.error'));
|
||||
}
|
||||
|
||||
|
||||
@@ -358,12 +378,19 @@ class LicensesController extends Controller
|
||||
public function getCheckin($seatId = null, $backTo = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($licenseseat = LicenseSeat::find($seatId))) {
|
||||
if (is_null($licenseSeat = LicenseSeat::find($seatId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
}
|
||||
$this->authorize('checkin', $licenseseat);
|
||||
return view('licenses/checkin', compact('licenseseat'))->with('backto', $backTo);
|
||||
|
||||
if (is_null($license = License::find($licenseSeat->license_id))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
return view('licenses/checkin', compact('licenseSeat'))->with('backto', $backTo);
|
||||
}
|
||||
|
||||
|
||||
@@ -386,8 +413,7 @@ class LicensesController extends Controller
|
||||
}
|
||||
|
||||
$license = License::find($licenseSeat->license_id);
|
||||
|
||||
$this->authorize('checkin', $licenseSeat);
|
||||
$this->authorize('checkout', $license);
|
||||
|
||||
if (!$license->reassignable) {
|
||||
// Not allowed to checkin
|
||||
@@ -395,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'));
|
||||
}
|
||||
@@ -440,15 +454,16 @@ class LicensesController extends Controller
|
||||
*/
|
||||
public function show($licenseId = null)
|
||||
{
|
||||
$license = License::find($licenseId);
|
||||
if (isset($license->id)) {
|
||||
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
|
||||
|
||||
$license = License::with('assignedusers', 'licenseSeats.user', 'licenseSeats.asset')->find($licenseId);
|
||||
|
||||
if ($license) {
|
||||
$this->authorize('view', $license);
|
||||
return view('licenses/view', compact('license'));
|
||||
}
|
||||
$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'));
|
||||
}
|
||||
|
||||
|
||||
public function getClone($licenseId = null)
|
||||
{
|
||||
@@ -471,11 +486,8 @@ class LicensesController extends Controller
|
||||
// Show the page
|
||||
return view('licenses/edit')
|
||||
->with('depreciation_list', Helper::depreciationList())
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('item', $license)
|
||||
->with('maintained_list', $maintained_list)
|
||||
->with('company_list', Helper::companyList())
|
||||
->with('manufacturer_list', Helper::manufacturerList());
|
||||
->with('maintained_list', $maintained_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +500,7 @@ class LicensesController extends Controller
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postUpload($licenseId = null)
|
||||
public function postUpload(AssetFileRequest $request, $licenseId = null)
|
||||
{
|
||||
$license = License::find($licenseId);
|
||||
// the license is valid
|
||||
@@ -497,37 +509,29 @@ 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
|
||||
$license->logUpload($filename, e($request->input('notes')));
|
||||
}
|
||||
|
||||
// This being called from a modal seems to confuse redirect()->back()
|
||||
// It thinks we should go to the dashboard. As this is only used
|
||||
// from the modal at present, hardcode the redirect. Longterm
|
||||
// maybe we evaluate something else.
|
||||
if ($upload_success) {
|
||||
return redirect()->back()->with('success', trans('admin/licenses/message.upload.success'));
|
||||
return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/licenses/message.upload.error'));
|
||||
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.error'));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/licenses/message.upload.nofiles'));
|
||||
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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -546,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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -574,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);
|
||||
@@ -583,64 +590,38 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
// 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'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a JSON response to populate the licence index datatables.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LicensesController::getIndex() method that provides the view
|
||||
* @since [v1.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable(Request $request)
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'licenseSeatsRelation', 'manufacturer'));
|
||||
|
||||
if (Input::has('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
}
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','manufacturer','company'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'manufacturer':
|
||||
$licenses = $licenses->OrderManufacturer($order);
|
||||
break;
|
||||
case 'company':
|
||||
$licenses = $licenses->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$licenses = $licenses->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$licenseCount = $licenses->count();
|
||||
$licenses = $licenses->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($licenses as $license) {
|
||||
$rows[] = $license->present()->forDataTable();
|
||||
}
|
||||
|
||||
$data = array('total' => $licenseCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the next free seat ID for checkout.
|
||||
|
||||
@@ -16,6 +16,8 @@ use Validator;
|
||||
use View;
|
||||
use Auth;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Image;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Locations for
|
||||
@@ -38,10 +40,9 @@ class LocationsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
// Grab all the locations
|
||||
$locations = Location::orderBy('created_at', 'DESC')->with('parent', 'assets', 'assignedassets')->get();
|
||||
|
||||
$this->authorize('view', Location::class);
|
||||
// Show the page
|
||||
return view('locations/index', compact('locations'));
|
||||
return view('locations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -55,16 +56,9 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$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;
|
||||
|
||||
$this->authorize('create', Location::class);
|
||||
return view('locations/edit')
|
||||
->with('location_options', $location_options)
|
||||
->with('item', new Location)
|
||||
->with('manager_list', Helper::managerList());
|
||||
->with('item', new Location);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,62 +71,31 @@ class LocationsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store()
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$location = new Location();
|
||||
$location->name = Input::get('name');
|
||||
$location->parent_id = Input::get('parent_id', null);
|
||||
$location->currency = Input::get('currency', '$');
|
||||
$location->address = Input::get('address');
|
||||
$location->address2 = Input::get('address2');
|
||||
$location->city = Input::get('city');
|
||||
$location->state = Input::get('state');
|
||||
$location->country = Input::get('country');
|
||||
$location->zip = Input::get('zip');
|
||||
$location->manager_id = Input::get('manager_id');
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
$location->currency = $request->input('currency', '$');
|
||||
$location->address = $request->input('address');
|
||||
$location->address2 = $request->input('address2');
|
||||
$location->city = $request->input('city');
|
||||
$location->state = $request->input('state');
|
||||
$location->country = $request->input('country');
|
||||
$location->zip = $request->input('zip');
|
||||
$location->ldap_ou = $request->input('ldap_ou');
|
||||
$location->manager_id = $request->input('manager_id');
|
||||
$location->user_id = Auth::id();
|
||||
|
||||
$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'));
|
||||
}
|
||||
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()
|
||||
{
|
||||
$new['currency']=Setting::first()->default_currency;
|
||||
|
||||
// create a new location instance
|
||||
$location = new Location();
|
||||
|
||||
// Save the location data
|
||||
$location->name = Input::get('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 = Input::get('city');
|
||||
$location->state = '';//e(Input::get('state'));
|
||||
$location->country = Input::get('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.
|
||||
@@ -145,21 +108,14 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function edit($locationId = null)
|
||||
{
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($item = Location::find($locationId))) {
|
||||
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -172,32 +128,37 @@ class LocationsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update($locationId = null)
|
||||
public function update(ImageUploadRequest $request, $locationId = null)
|
||||
{
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
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 = Input::get('name');
|
||||
$location->parent_id = Input::get('parent_id', null);
|
||||
$location->currency = Input::get('currency', '$');
|
||||
$location->address = Input::get('address');
|
||||
$location->address2 = Input::get('address2');
|
||||
$location->city = Input::get('city');
|
||||
$location->state = Input::get('state');
|
||||
$location->country = Input::get('country');
|
||||
$location->zip = Input::get('zip');
|
||||
$location->ldap_ou = Input::get('ldap_ou');
|
||||
$location->manager_id = Input::get('manager_id');
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
$location->currency = $request->input('currency', '$');
|
||||
$location->address = $request->input('address');
|
||||
$location->address2 = $request->input('address2');
|
||||
$location->city = $request->input('city');
|
||||
$location->state = $request->input('state');
|
||||
$location->country = $request->input('country');
|
||||
$location->zip = $request->input('zip');
|
||||
$location->ldap_ou = $request->input('ldap_ou');
|
||||
$location->manager_id = $request->input('manager_id');
|
||||
$location = $request->handleImages($location,600, public_path().'/uploads/locations');
|
||||
|
||||
|
||||
// Was the location updated?
|
||||
if ($location->save()) {
|
||||
// Redirect to the saved location page
|
||||
return redirect()->route("locations.index")->with('success', trans('admin/locations/message.update.success'));
|
||||
}
|
||||
// Redirect to the location management page
|
||||
return redirect()->back()->withInput()->withInput()->withErrors($location->getErrors());
|
||||
}
|
||||
|
||||
@@ -211,21 +172,23 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function destroy($locationId)
|
||||
{
|
||||
// Check if the location exists
|
||||
$this->authorize('delete', Location::class);
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
|
||||
} elseif ($location->assignedassets->count() > 0) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
|
||||
} else {
|
||||
$location->delete();
|
||||
return redirect()->to(route('locations.index'))->with('success', trans('admin/locations/message.delete.success'));
|
||||
@@ -249,11 +212,8 @@ class LocationsController extends Controller
|
||||
if (isset($location->id)) {
|
||||
return view('locations/view', compact('location'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/locations/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('locations.index')->with('error', $error);
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Manufacturer;
|
||||
use Auth;
|
||||
@@ -13,6 +14,7 @@ use Redirect;
|
||||
use Str;
|
||||
use View;
|
||||
use Illuminate\Http\Request;
|
||||
use Image;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Manufacturers for
|
||||
@@ -33,7 +35,8 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('manufacturers/index', compact('manufacturers'));
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
return view('manufacturers/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +50,7 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
return view('manufacturers/edit')->with('item', new Manufacturer);
|
||||
}
|
||||
|
||||
@@ -60,9 +64,10 @@ class ManufacturersController extends Controller
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->name = $request->input('name');
|
||||
$manufacturer->user_id = Auth::user()->id;
|
||||
@@ -70,6 +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');
|
||||
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
|
||||
|
||||
|
||||
|
||||
@@ -90,10 +96,14 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
// 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'));
|
||||
}
|
||||
@@ -109,8 +119,9 @@ class ManufacturersController extends Controller
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function update(Request $request, $manufacturerId = null)
|
||||
public function update(ImageUploadRequest $request, $manufacturerId = null)
|
||||
{
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
|
||||
// Redirect to the manufacturer page
|
||||
@@ -123,6 +134,15 @@ 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');
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$manufacturer->image = null;
|
||||
}
|
||||
|
||||
$manufacturer = $request->handleImages($manufacturer,600, public_path().'/uploads/manufacturers');
|
||||
|
||||
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.update.success'));
|
||||
@@ -140,6 +160,7 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function destroy($manufacturerId)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
|
||||
// Redirect to the manufacturers page
|
||||
@@ -150,6 +171,16 @@ class ManufacturersController extends Controller
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.assoc_users'));
|
||||
}
|
||||
|
||||
if ($manufacturer->image) {
|
||||
try {
|
||||
unlink(public_path().'/uploads/manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delete the manufacturer
|
||||
$manufacturer->delete();
|
||||
// Redirect to the manufacturers management page
|
||||
@@ -158,170 +189,55 @@ class ManufacturersController extends Controller
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the manufacturers detail listing, which is generated in getDatatable.
|
||||
* the content for the manufacturers detail listing, which is generated via API.
|
||||
* This data contains a listing of all assets that belong to that manufacturer.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getDataView()
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($manufacturerId = null)
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::find($manufacturerId);
|
||||
|
||||
if (isset($manufacturer->id)) {
|
||||
return view('manufacturers/view', compact('manufacturer'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/manufacturers/message.does_not_exist', compact('id'));
|
||||
|
||||
$error = trans('admin/manufacturers/message.does_not_exist');
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('manufacturers')->with('error', $error);
|
||||
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'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON used to display the manufacturer detail.
|
||||
* This JSON returns data on all of the assets with the specified
|
||||
* manufacturer ID number.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getView()
|
||||
* @param int $manufacturerId
|
||||
* @param string $itemType
|
||||
* @param Request $request
|
||||
* @return String JSON* @since [v1.0]
|
||||
*/
|
||||
public function getDataView($manufacturerId, $itemType = null, Request $request)
|
||||
{
|
||||
$manufacturer = Manufacturer::find($manufacturerId);
|
||||
|
||||
switch ($itemType) {
|
||||
case "assets":
|
||||
return $this->getDataAssetsView($manufacturer, $request);
|
||||
case "licenses":
|
||||
return $this->getDataLicensesView($manufacturer, $request);
|
||||
case "accessories":
|
||||
return $this->getDataAccessoriesView($manufacturer, $request);
|
||||
case "consumables":
|
||||
return $this->getDataConsumablesView($manufacturer, $request);
|
||||
}
|
||||
|
||||
return "We shouldn't be here";
|
||||
|
||||
}
|
||||
|
||||
protected function getDataAssetsView(Manufacturer $manufacturer, Request $request)
|
||||
{
|
||||
$manufacturer = $manufacturer->load('assets.model', 'assets.assignedTo', 'assets.assetstatus', 'assets.company');
|
||||
$manufacturer_assets = $manufacturer->assets();
|
||||
|
||||
if ($request->has('search')) {
|
||||
$manufacturer_assets = $manufacturer_assets->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$count = $manufacturer_assets->count();
|
||||
$manufacturer_assets = $manufacturer_assets->skip($offset)->take($limit)->get();
|
||||
$rows = array();
|
||||
$all_custom_fields = CustomField::all(); // cached;
|
||||
foreach ($manufacturer_assets as $asset) {
|
||||
$rows[] = $asset->present()->forDataTable($all_custom_fields);
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getDataLicensesView(Manufacturer $manufacturer, Request $request)
|
||||
{
|
||||
$manufacturer = $manufacturer->load('licenses.company', 'licenses.manufacturer', 'licenses.licenseSeatsRelation');
|
||||
$licenses = $manufacturer->licenses;
|
||||
|
||||
if ($request->has('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$licenseCount = $licenses->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($licenses as $license) {
|
||||
$rows[] = $license->present()->forDataTable();
|
||||
}
|
||||
|
||||
$data = array('total' => $licenseCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataAccessoriesView(Manufacturer $manufacturer, Request $request)
|
||||
{
|
||||
$manufacturer = $manufacturer->load(
|
||||
'accessories.location',
|
||||
'accessories.company',
|
||||
'accessories.category',
|
||||
'accessories.manufacturer',
|
||||
'accessories.users'
|
||||
);
|
||||
$accessories = $manufacturer->accessories();
|
||||
|
||||
if ($request->has('search')) {
|
||||
$accessories = $accessories->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$accessCount = $accessories->count();
|
||||
$accessories = $accessories->skip($offset)->take($limit)->get();
|
||||
$rows = array();
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$rows[] = $accessory->present()->forDataTable();
|
||||
}
|
||||
|
||||
$data = array('total'=>$accessCount, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataConsumablesView($manufacturer, Request $request)
|
||||
{
|
||||
$manufacturer = $manufacturer->load(
|
||||
'consumables.location',
|
||||
'consumables.company',
|
||||
'consumables.category',
|
||||
'consumables.manufacturer',
|
||||
'consumables.users'
|
||||
);
|
||||
$consumables = $manufacturer->consumables();
|
||||
|
||||
if ($request->has('search')) {
|
||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
|
||||
$consumCount = $consumables->count();
|
||||
$consumables = $consumables->skip($offset)->take($limit)->get();
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumables as $consumable) {
|
||||
$rows[] = $consumable->present()->forDataTable();
|
||||
}
|
||||
|
||||
$data = array('total' => $consumCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
@@ -29,4 +27,13 @@ class ModalController extends Controller
|
||||
function user() {
|
||||
return view('modals.user');
|
||||
}
|
||||
|
||||
function category() {
|
||||
return view('modals.category');
|
||||
}
|
||||
|
||||
function manufacturer() {
|
||||
return view('modals.manufacturer');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\Setting;
|
||||
use Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Profiles for
|
||||
@@ -30,8 +31,7 @@ class ProfileController extends Controller
|
||||
public function getIndex()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$location_list = Helper::locationsList();
|
||||
return view('account/profile', compact('user'))->with('location_list', $location_list);
|
||||
return view('account/profile', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,19 +41,28 @@ class ProfileController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postIndex()
|
||||
public function postIndex(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$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')) {
|
||||
@@ -109,18 +118,17 @@ class ProfileController extends Controller
|
||||
{
|
||||
|
||||
if (config('app.lock_passwords')) {
|
||||
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/table.lock_passwords'));
|
||||
return redirect()->route('account.password.index')->with('error', trans('admin/users/table.lock_passwords'));
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
if ($user->ldap_import=='1') {
|
||||
return redirect()->route('account.password.index')->with('error', Lang::get('admin/users/message.error.password_ldap'));
|
||||
return redirect()->route('account.password.index')->with('error', trans('admin/users/message.error.password_ldap'));
|
||||
}
|
||||
|
||||
$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);
|
||||
@@ -143,5 +151,25 @@ class ProfileController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the menu state of open/closed when the user clicks on the hamburger
|
||||
* menu.
|
||||
*
|
||||
* This URL is triggered via jquery in
|
||||
* resources/views/layouts/default.blade.php
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
|
||||
public function getMenuState(Request $request) {
|
||||
if ($request->input('state')=='open') {
|
||||
$request->session()->put('menu_state', 'open');
|
||||
} else {
|
||||
$request->session()->put('menu_state', 'closed');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetMaintenance;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Depreciation;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use Carbon\Carbon;
|
||||
@@ -15,6 +16,7 @@ use Illuminate\Support\Facades\View;
|
||||
use Input;
|
||||
use League\Csv\Reader;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Reports for
|
||||
@@ -24,6 +26,14 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
*/
|
||||
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.
|
||||
@@ -80,101 +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()
|
||||
{
|
||||
|
||||
\Debugbar::disable();
|
||||
|
||||
$customfields = CustomField::get();
|
||||
|
||||
$response = new StreamedResponse(function () use ($customfields) {
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
|
||||
Asset::with('assignedTo', 'assetLoc','defaultLoc','assignedTo','model','supplier','assetstatus','model.manufacturer')->orderBy('created_at', 'DESC')->chunk(500, function($assets) use($handle, $customfields) {
|
||||
$headers=[
|
||||
trans('general.company'),
|
||||
trans('admin/hardware/table.asset_tag'),
|
||||
trans('admin/hardware/form.manufacturer'),
|
||||
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('admin/hardware/form.supplier'),
|
||||
trans('admin/hardware/table.checkoutto'),
|
||||
trans('admin/hardware/table.checkout_date'),
|
||||
trans('admin/hardware/table.location'),
|
||||
trans('general.notes'),
|
||||
];
|
||||
foreach ($customfields as $field) {
|
||||
$headers[]=$field->name;
|
||||
}
|
||||
fputcsv($handle, $headers);
|
||||
|
||||
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) ? $asset->model->name : '',
|
||||
($asset->model->model_number) ? $asset->model->model_number : '',
|
||||
($asset->name) ? $asset->name : '',
|
||||
($asset->serial) ? $asset->serial : '',
|
||||
($asset->assetstatus) ? e($asset->assetstatus->name) : '',
|
||||
($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->assignedTo) ? e($asset->assignedTo->present()->name()) : '',
|
||||
($asset->last_checkout!='') ? e($asset->last_checkout) : '',
|
||||
e($asset->assetLoc->present()->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="assets-'.date('Y-m-d-his').'.csv"',
|
||||
]);
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show depreciation report for assets.
|
||||
*
|
||||
@@ -185,11 +100,12 @@ class ReportsController extends Controller
|
||||
public function getDeprecationReport()
|
||||
{
|
||||
|
||||
$depreciations = Depreciation::get();
|
||||
// Grab all the assets
|
||||
$assets = Asset::with('model', 'assignedTo', 'assetstatus', 'defaultLoc', 'assetlog', 'company')
|
||||
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'assetlog', 'company', 'model.category', 'model.depreciation')
|
||||
->orderBy('created_at', 'DESC')->get();
|
||||
|
||||
return view('reports/depreciation', compact('assets'));
|
||||
return view('reports/depreciation', compact('assets'))->with('depreciations',$depreciations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,7 +157,7 @@ class ReportsController extends Controller
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
|
||||
if (( $asset->assigned_to > 0 ) && ( $location = $asset->assetLoc )) {
|
||||
if (( $asset->assigned_to > 0 ) && ( $location = $asset->location )) {
|
||||
if ($location->city) {
|
||||
$row[] = e($location->city) . ', ' . e($location->state);
|
||||
} elseif ($location->name) {
|
||||
@@ -253,8 +169,8 @@ class ReportsController extends Controller
|
||||
$row[] = ''; // Empty string if location is not set
|
||||
}
|
||||
|
||||
if ($asset->assetloc) {
|
||||
$currency = e($asset->assetloc->currency);
|
||||
if ($asset->location) {
|
||||
$currency = e($asset->location->currency);
|
||||
} else {
|
||||
$currency = e(Setting::first()->default_currency);
|
||||
}
|
||||
@@ -388,251 +304,432 @@ class ReportsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function postCustom()
|
||||
public function postCustom(Request $request)
|
||||
{
|
||||
$assets = Asset::orderBy('created_at', 'DESC')->with('company', 'assigneduser', 'assetloc', 'defaultLoc', 'assigneduser.userloc', 'model', 'supplier', 'assetstatus', 'model.manufacturer')->get();
|
||||
|
||||
ini_set('max_execution_time', 12000);
|
||||
|
||||
|
||||
\Debugbar::disable();
|
||||
$customfields = CustomField::get();
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
|
||||
$rows = [ ];
|
||||
$header = [ ];
|
||||
\Log::debug('Starting streamed response');
|
||||
|
||||
if (e(Input::get('company')) == '1') {
|
||||
$header[] = 'Company Name';
|
||||
}
|
||||
|
||||
if (e(Input::get('asset_name')) == '1') {
|
||||
$header[] = 'Asset Name';
|
||||
}
|
||||
if (e(Input::get('asset_tag')) == '1') {
|
||||
$header[] = 'Asset Tag';
|
||||
}
|
||||
if (e(Input::get('manufacturer')) == '1') {
|
||||
$header[] = 'Manufacturer';
|
||||
}
|
||||
if (e(Input::get('model')) == '1') {
|
||||
$header[] = 'Model';
|
||||
$header[] = 'Model Number';
|
||||
}
|
||||
if (e(Input::get('category')) == '1') {
|
||||
$header[] = 'Category';
|
||||
}
|
||||
if (e(Input::get('serial')) == '1') {
|
||||
$header[] = 'Serial';
|
||||
}
|
||||
if (e(Input::get('purchase_date')) == '1') {
|
||||
$header[] = 'Purchase Date';
|
||||
}
|
||||
if (( e(Input::get('purchase_cost')) == '1' ) && ( e(Input::get('depreciation')) != '1' )) {
|
||||
$header[] = 'Purchase Cost';
|
||||
}
|
||||
if (e(Input::get('eol')) == '1') {
|
||||
$header[] = 'EOL';
|
||||
}
|
||||
if (e(Input::get('order')) == '1') {
|
||||
$header[] = 'Order Number';
|
||||
}
|
||||
if (e(Input::get('supplier')) == '1') {
|
||||
$header[] = 'Supplier';
|
||||
}
|
||||
if (e(Input::get('location')) == '1') {
|
||||
$header[] = 'Location';
|
||||
}
|
||||
if (e(Input::get('assigned_to')) == '1') {
|
||||
$header[] = 'Assigned To';
|
||||
}
|
||||
if (e(Input::get('username')) == '1') {
|
||||
$header[] = 'Username';
|
||||
}
|
||||
if (e(Input::get('employee_num')) == '1') {
|
||||
$header[] = 'Employee No.';
|
||||
}
|
||||
if (e(Input::get('status')) == '1') {
|
||||
$header[] = 'Status';
|
||||
}
|
||||
if (e(Input::get('warranty')) == '1') {
|
||||
$header[] = 'Warranty';
|
||||
$header[] = 'Warranty Expires';
|
||||
}
|
||||
if (e(Input::get('depreciation')) == '1') {
|
||||
$header[] = 'Purchase Cost';
|
||||
$header[] = 'Value';
|
||||
$header[] = 'Diff';
|
||||
}
|
||||
if (e(Input::get('expected_checkin')) == '1') {
|
||||
$header[] = trans('admin/hardware/form.expected_checkin');
|
||||
}
|
||||
|
||||
if (e(Input::get('notes')) == '1') {
|
||||
$header[] = 'Notes';
|
||||
}
|
||||
|
||||
|
||||
foreach ($customfields as $customfield) {
|
||||
if (e(Input::get($customfield->db_column_name())) == '1') {
|
||||
$header[] = $customfield->name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$header = array_map('trim', $header);
|
||||
$rows[] = implode($header, ',');
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$row = [ ];
|
||||
|
||||
if (e(Input::get('company')) == '1') {
|
||||
$row[] = is_null($asset->company) ? '' : '"'.$asset->company->name.'"';
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
stream_set_timeout($handle, 2000);
|
||||
|
||||
if ($request->filled('use_bom')) {
|
||||
fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
}
|
||||
|
||||
if (e(Input::get('asset_name')) == '1') {
|
||||
$row[] = '"' .e($asset->name) . '"';
|
||||
}
|
||||
if (e(Input::get('asset_tag')) == '1') {
|
||||
$row[] = e($asset->asset_tag);
|
||||
}
|
||||
if (e(Input::get('manufacturer')) == '1') {
|
||||
if ($asset->model->manufacturer) {
|
||||
$row[] = '"' .e($asset->model->manufacturer->name) . '"';
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
if (e(Input::get('model')) == '1') {
|
||||
$row[] = '"' . e($asset->model->name) . '"';
|
||||
$row[] = '"' . e($asset->model->model_number) . '"';
|
||||
}
|
||||
if (e(Input::get('category')) == '1') {
|
||||
$row[] = '"' .e($asset->model->category->name) . '"';
|
||||
$header = [];
|
||||
|
||||
|
||||
if ($request->filled('company')) {
|
||||
$header[] = trans('general.company');
|
||||
}
|
||||
|
||||
if (e(Input::get('serial')) == '1') {
|
||||
$row[] = e($asset->serial);
|
||||
}
|
||||
if (e(Input::get('purchase_date')) == '1') {
|
||||
$row[] = e($asset->purchase_date);
|
||||
}
|
||||
if (e(Input::get('purchase_cost')) == '1' && ( e(Input::get('depreciation')) != '1' )) {
|
||||
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
|
||||
}
|
||||
if (e(Input::get('eol')) == '1') {
|
||||
$row[] = '"' .($asset->present()->eol_date()) ? $asset->present()->eol_date() : ''. '"';
|
||||
}
|
||||
if (e(Input::get('order')) == '1') {
|
||||
if ($asset->order_number) {
|
||||
$row[] = e($asset->order_number);
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
if (e(Input::get('supplier')) == '1') {
|
||||
if ($asset->supplier) {
|
||||
$row[] = '"' .e($asset->supplier->name) . '"';
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
if ($request->filled('asset_name')) {
|
||||
$header[] = trans('admin/hardware/form.name');
|
||||
}
|
||||
|
||||
if (e(Input::get('location')) == '1') {
|
||||
if($asset->assetLoc) {
|
||||
$show_loc = $asset->assetLoc->present()->name();
|
||||
} else {
|
||||
$show_loc = 'Default location '.$asset->rtd_location_id.' is invalid';
|
||||
}
|
||||
$row[] = $show_loc;
|
||||
if ($request->filled('asset_tag')) {
|
||||
$header[] = trans('admin/hardware/table.asset_tag');
|
||||
}
|
||||
|
||||
if ($request->filled('model')) {
|
||||
$header[] = trans('admin/hardware/form.model');
|
||||
$header[] = trans('general.model_no');
|
||||
}
|
||||
|
||||
if ($request->filled('category')) {
|
||||
$header[] = trans('general.category');
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer')) {
|
||||
$header[] = trans('admin/hardware/form.manufacturer');
|
||||
}
|
||||
|
||||
if ($request->filled('serial')) {
|
||||
$header[] = trans('admin/hardware/table.serial');
|
||||
}
|
||||
if ($request->filled('purchase_date')) {
|
||||
$header[] = trans('admin/hardware/table.purchase_date');
|
||||
}
|
||||
|
||||
if (($request->filled('purchase_cost')) || ($request->filled('depreciation'))) {
|
||||
$header[] = trans('admin/hardware/table.purchase_cost');
|
||||
}
|
||||
|
||||
if ($request->filled('eol')) {
|
||||
$header[] = trans('admin/hardware/table.eol');
|
||||
}
|
||||
|
||||
if ($request->filled('order')) {
|
||||
$header[] = trans('admin/hardware/form.order');
|
||||
}
|
||||
|
||||
if ($request->filled('supplier')) {
|
||||
$header[] = trans('general.supplier');
|
||||
}
|
||||
|
||||
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->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 (e(Input::get('assigned_to')) == '1') {
|
||||
if ($asset->assignedTo) {
|
||||
$row[] = '"' .e($asset->assignedTo->present()->name()). '"';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
if ($request->filled('assigned_to')) {
|
||||
$header[] = trans('admin/hardware/table.checkoutto');
|
||||
$header[] = trans('general.type');
|
||||
}
|
||||
|
||||
if (e(Input::get('username')) == '1') {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->assigneduser) {
|
||||
$row[] = '"' .e($asset->assigneduser->username). '"';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
if ($request->filled('username')) {
|
||||
$header[] = 'Username';
|
||||
}
|
||||
|
||||
if (e(Input::get('employee_num')) == '1') {
|
||||
// Only works if we're checked out to a user, not anything else.
|
||||
if ($asset->assigneduser) {
|
||||
$row[] = '"' .e($asset->assigneduser->employee_num). '"';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
if ($request->filled('employee_num')) {
|
||||
$header[] = 'Employee No.';
|
||||
}
|
||||
|
||||
if (e(Input::get('status')) == '1') {
|
||||
if (( $asset->status_id == '0' ) && ( $asset->assigned_to == '0' )) {
|
||||
$row[] = trans('general.ready_to_deploy');
|
||||
} elseif (( $asset->status_id == '' ) && ( $asset->assigned_to == '0' )) {
|
||||
$row[] = trans('general.pending');
|
||||
} elseif ($asset->assetstatus) {
|
||||
$row[] = '"' .e($asset->assetstatus->name). '"';
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
if (e(Input::get('warranty')) == '1') {
|
||||
if ($asset->warranty_months) {
|
||||
$row[] = $asset->warranty_months;
|
||||
$row[] = $asset->present()->warrantee_expires();
|
||||
} else {
|
||||
$row[] = '';
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
if (e(Input::get('depreciation')) == '1') {
|
||||
$depreciation = $asset->getDepreciatedValue();
|
||||
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
|
||||
$row[] = '"' . Helper::formatCurrencyOutput($depreciation) . '"';
|
||||
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
|
||||
}
|
||||
if (e(Input::get('expected_checkin')) == '1') {
|
||||
if ($asset->expected_checkin) {
|
||||
$row[] = '"' .e($asset->expected_checkin). '"';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if blankd
|
||||
}
|
||||
if ($request->filled('manager')) {
|
||||
$header[] = trans('admin/users/table.manager');
|
||||
}
|
||||
|
||||
if (e(Input::get('notes')) == '1') {
|
||||
if ($asset->notes) {
|
||||
$row[] = '"' .$asset->notes. '"';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
if ($request->filled('department')) {
|
||||
$header[] = trans('general.department');
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$header[] = trans('general.status');
|
||||
}
|
||||
|
||||
if ($request->filled('warranty')) {
|
||||
$header[] = 'Warranty';
|
||||
$header[] = 'Warranty Expires';
|
||||
}
|
||||
if ($request->filled('depreciation')) {
|
||||
$header[] = 'Value';
|
||||
$header[] = 'Diff';
|
||||
}
|
||||
|
||||
if ($request->filled('checkout_date')) {
|
||||
$header[] = trans('admin/hardware/table.checkout_date');
|
||||
}
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$header[] = trans('admin/hardware/form.expected_checkin');
|
||||
}
|
||||
|
||||
if ($request->filled('created_at')) {
|
||||
$header[] = trans('general.created_at');
|
||||
}
|
||||
|
||||
if ($request->filled('updated_at')) {
|
||||
$header[] = trans('general.updated_at');
|
||||
}
|
||||
|
||||
if ($request->filled('last_audit_date')) {
|
||||
$header[] = trans('general.last_audit');
|
||||
}
|
||||
|
||||
if ($request->filled('next_audit_date')) {
|
||||
$header[] = trans('general.next_audit_date');
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$header[] = trans('general.notes');
|
||||
}
|
||||
|
||||
|
||||
foreach ($customfields as $customfield) {
|
||||
$column_name = $customfield->db_column_name();
|
||||
if (e(Input::get($customfield->db_column_name())) == '1') {
|
||||
$row[] = str_replace(",", "\,", $asset->$column_name);
|
||||
$header[] = $customfield->name;
|
||||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
$rows[] = implode($row, ',');
|
||||
}
|
||||
|
||||
// spit out a csv
|
||||
if (array_filter($rows)) {
|
||||
$csv = implode($rows, "\n");
|
||||
$response = Response::make($csv, 200);
|
||||
$response->header('Content-Type', 'text/csv');
|
||||
$response->header('Content-disposition', 'attachment;filename='.date('Y-m-d-His').'-custom-asset-report.csv');
|
||||
$assets = \App\Models\Company::scopeCompanyables(Asset::select('assets.*'))->with(
|
||||
'location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer','supplier');
|
||||
|
||||
if ($request->filled('by_location_id')) {
|
||||
$assets->where('assets.location_id', $request->input('by_location_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->filled('by_company_id')) {
|
||||
$assets->where('assets.company_id', $request->input('by_company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_model_id')) {
|
||||
$assets->where('assets.model_id', $request->input('by_model_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_category_id')) {
|
||||
$assets->InCategory($request->input('by_category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_manufacturer_id')) {
|
||||
$assets->ByManufacturer($request->input('by_manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_order_number')) {
|
||||
$assets->where('assets.order_number', $request->input('by_order_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('by_status_id')) {
|
||||
$assets->where('assets.status_id', $request->input('by_status_id'));
|
||||
}
|
||||
|
||||
if (($request->filled('purchase_start')) && ($request->filled('purchase_end'))) {
|
||||
$assets->whereBetween('assets.purchase_date', [$request->input('purchase_start'), $request->input('purchase_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('created_start')) && ($request->filled('created_end'))) {
|
||||
$assets->whereBetween('assets.created_at', [$request->input('created_start'), $request->input('created_end')]);
|
||||
}
|
||||
|
||||
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->filled('company')) {
|
||||
$row[] = ($asset->company) ? $asset->company->name : '';
|
||||
}
|
||||
|
||||
if ($request->filled('asset_name')) {
|
||||
$row[] = ($asset->name) ? $asset->name : '';
|
||||
}
|
||||
|
||||
if ($request->filled('asset_tag')) {
|
||||
$row[] = ($asset->asset_tag) ? $asset->asset_tag : '';
|
||||
}
|
||||
|
||||
if ($request->filled('model')) {
|
||||
$row[] = ($asset->model) ? $asset->model->name : '';
|
||||
$row[] = ($asset->model) ? $asset->model->model_number : '';
|
||||
}
|
||||
|
||||
if ($request->filled('category')) {
|
||||
$row[] = (($asset->model) && ($asset->model->category)) ? $asset->model->category->name : '';
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer')) {
|
||||
$row[] = ($asset->model && $asset->model->manufacturer) ? $asset->model->manufacturer->name : '';
|
||||
}
|
||||
|
||||
if ($request->filled('serial')) {
|
||||
$row[] = ($asset->serial) ? $asset->serial : '';
|
||||
}
|
||||
|
||||
if ($request->filled('purchase_date')) {
|
||||
$row[] = ($asset->purchase_date) ? $asset->purchase_date : '';
|
||||
}
|
||||
|
||||
if ($request->filled('purchase_cost')) {
|
||||
$row[] = ($asset->purchase_cost) ? Helper::formatCurrencyOutput($asset->purchase_cost) : '';
|
||||
}
|
||||
|
||||
if ($request->filled('eol')) {
|
||||
$row[] = ($asset->purchase_date!='') ? $asset->present()->eol_date() : '';
|
||||
}
|
||||
|
||||
if ($request->filled('order')) {
|
||||
$row[] = ($asset->order_number) ? $asset->order_number : '';
|
||||
}
|
||||
|
||||
if ($request->filled('supplier')) {
|
||||
$row[] = ($asset->supplier) ? $asset->supplier->name : '';
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('location')) {
|
||||
$row[] = ($asset->location) ? $asset->location->present()->name() : '';
|
||||
}
|
||||
|
||||
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->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 : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
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 : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
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->filled('warranty')) {
|
||||
$row[] = ($asset->warranty_months) ? $asset->warranty_months : '';
|
||||
$row[] = $asset->present()->warrantee_expires();
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('depreciation')) {
|
||||
$depreciation = $asset->getDepreciatedValue();
|
||||
$diff = ($asset->purchase_cost - $depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($diff);
|
||||
}
|
||||
|
||||
if ($request->filled('checkout_date')) {
|
||||
$row[] = ($asset->last_checkout) ? $asset->last_checkout : '';
|
||||
}
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$row[] = ($asset->expected_checkin) ? $asset->expected_checkin : '';
|
||||
}
|
||||
|
||||
if ($request->filled('created_at')) {
|
||||
$row[] = ($asset->created_at) ? $asset->created_at : '';
|
||||
}
|
||||
|
||||
if ($request->filled('updated_at')) {
|
||||
$row[] = ($asset->updated_at) ? $asset->updated_at : '';
|
||||
}
|
||||
|
||||
if ($request->filled('last_audit_date')) {
|
||||
$row[] = ($asset->last_audit_date) ? $asset->last_audit_date : '';
|
||||
}
|
||||
|
||||
if ($request->filled('next_audit_date')) {
|
||||
$row[] = ($asset->next_audit_date) ? $asset->next_audit_date : '';
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$row[] = ($asset->notes) ? $asset->notes : '';
|
||||
}
|
||||
|
||||
foreach ($customfields as $customfield) {
|
||||
$column_name = $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;
|
||||
|
||||
|
||||
return $response;
|
||||
} else {
|
||||
return redirect()->to("reports/custom")
|
||||
->with('error', trans('admin/reports/message.error'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -20,6 +21,9 @@ use Auth;
|
||||
use App\Models\User;
|
||||
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
|
||||
@@ -55,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'];
|
||||
|
||||
@@ -65,7 +70,7 @@ class SettingsController extends Controller
|
||||
|
||||
$start_settings['url_config'] = url('/');
|
||||
$start_settings['real_url'] = $pageURL;
|
||||
|
||||
|
||||
// Curl the .env file to make sure it's not accessible via a browser
|
||||
$ch = curl_init($protocol . $host.'/.env');
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // we want headers
|
||||
@@ -135,28 +140,6 @@ class SettingsController extends Controller
|
||||
->with('section', 'Pre-Flight Check');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the email configuration
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function ajaxTestEmail()
|
||||
{
|
||||
|
||||
try {
|
||||
Mail::send('emails.test', [], function ($m) {
|
||||
$m->to(config('mail.from.address'), config('mail.from.name'));
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.test_email'));
|
||||
});
|
||||
return 'success';
|
||||
} catch (Exception $e) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the first admin user from Setup.
|
||||
@@ -170,28 +153,31 @@ class SettingsController extends Controller
|
||||
|
||||
|
||||
$user = new User;
|
||||
$user->first_name = $data['first_name']= e(Input::get('first_name'));
|
||||
$user->last_name = e(Input::get('last_name'));
|
||||
$user->email = $data['email'] = e(Input::get('email'));
|
||||
$user->first_name = $data['first_name']= $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->email = $data['email'] = $request->input('email');
|
||||
$user->activated = 1;
|
||||
$permissions = array('superuser' => 1);
|
||||
$user->permissions = json_encode($permissions);
|
||||
$user->username = $data['username'] = e(Input::get('username'));
|
||||
$user->password = bcrypt(Input::get('password'));
|
||||
$data['password'] = Input::get('password');
|
||||
$user->username = $data['username'] = $request->input('username');
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
$data['password'] = $request->input('password');
|
||||
|
||||
$settings = new Setting;
|
||||
$settings->site_name = e(Input::get('site_name'));
|
||||
$settings->alert_email = e(Input::get('email'));
|
||||
$settings->full_multiple_companies_support = $request->input('full_multiple_companies_support', 0);
|
||||
$settings->site_name = $request->input('site_name');
|
||||
$settings->alert_email = $request->input('email');
|
||||
$settings->alerts_enabled = 1;
|
||||
$settings->pwd_secure_min = 10;
|
||||
$settings->brand = 1;
|
||||
$settings->locale = 'en';
|
||||
$settings->default_currency = 'USD';
|
||||
$settings->locale = $request->input('locale', 'en');
|
||||
$settings->default_currency = $request->input('default_currency', "USD");
|
||||
$settings->user_id = 1;
|
||||
$settings->email_domain = e(Input::get('email_domain'));
|
||||
$settings->email_format = e(Input::get('email_format'));
|
||||
$settings->email_domain = $request->input('email_domain');
|
||||
$settings->email_format = $request->input('email_format');
|
||||
$settings->next_auto_tag_base = 1;
|
||||
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
|
||||
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
|
||||
|
||||
|
||||
if ((!$user->isValid()) || (!$settings->isValid())) {
|
||||
@@ -202,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'));
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
|
||||
@@ -262,8 +256,8 @@ class SettingsController extends Controller
|
||||
$output = Artisan::output();
|
||||
|
||||
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
|
||||
Artisan::call('passport:install');
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
Artisan::call('passport:install');
|
||||
}
|
||||
|
||||
|
||||
@@ -331,15 +325,31 @@ class SettingsController extends Controller
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->modellist_displays = '';
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
|
||||
$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');
|
||||
$setting->email_format = $request->input('email_format');
|
||||
$setting->username_format = $request->input('username_format');
|
||||
$setting->require_accept_signature = $request->input('require_accept_signature');
|
||||
$setting->login_note = $request->input('login_note');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$setting->login_note = $request->input('login_note');
|
||||
}
|
||||
|
||||
$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');
|
||||
@@ -387,6 +397,13 @@ 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
|
||||
@@ -410,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, 40, function ($constraint) {
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -468,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'));
|
||||
}
|
||||
|
||||
@@ -517,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');
|
||||
@@ -561,11 +596,17 @@ 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 = e($alert_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');
|
||||
$setting->audit_interval = $request->input('audit_interval');
|
||||
$setting->audit_warning_days = $request->input('audit_warning_days');
|
||||
$setting->show_alerts_in_menu = $request->input('show_alerts_in_menu', '0');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
@@ -604,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());
|
||||
|
||||
}
|
||||
@@ -761,25 +812,38 @@ class SettingsController extends Controller
|
||||
$setting->labels_fontsize = $request->input('labels_fontsize');
|
||||
$setting->labels_pagewidth = $request->input('labels_pagewidth');
|
||||
$setting->labels_pageheight = $request->input('labels_pageheight');
|
||||
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
|
||||
|
||||
|
||||
|
||||
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()) {
|
||||
@@ -823,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');
|
||||
@@ -840,6 +904,7 @@ class SettingsController extends Controller
|
||||
$setting->is_ad = $request->input('is_ad', '0');
|
||||
$setting->ldap_tls = $request->input('ldap_tls', '0');
|
||||
$setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0');
|
||||
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
@@ -864,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();
|
||||
|
||||
@@ -901,12 +966,30 @@ class SettingsController extends Controller
|
||||
|
||||
public function postBackups()
|
||||
{
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
Artisan::call('backup:run');
|
||||
return redirect()->route('settings.backups.index')->with('success', trans('admin/settings/message.backup.generated'));
|
||||
} else {
|
||||
return redirect()->to("settings.backups.index")->with('error', trans('general.feature_disabled'));
|
||||
}
|
||||
$output = Artisan::output();
|
||||
|
||||
// Backup completed
|
||||
if (!preg_match('/failed/', $output)) {
|
||||
return redirect()->route('settings.backups.index')
|
||||
->with('success', trans('admin/settings/message.backup.generated'));
|
||||
}
|
||||
|
||||
|
||||
$formatted_output = str_replace('Backup completed!', '', $output);
|
||||
$output_split = explode('...', $formatted_output);
|
||||
|
||||
if (array_key_exists(2, $output_split)) {
|
||||
return redirect()->route("settings.backups.index")->with('error', $output_split[2]);
|
||||
}
|
||||
return redirect()->route("settings.backups.index")->with('error', $formatted_output);
|
||||
|
||||
}
|
||||
return redirect()->route("settings.backups.index")->with('error', trans('general.feature_disabled'));
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -922,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);
|
||||
@@ -951,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);
|
||||
@@ -989,6 +1072,8 @@ class SettingsController extends Controller
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
if (Input::get('confirm_purge')=='DELETE') {
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
Artisan::call('snipeit:purge', ['--force'=>'true','--no-interaction'=>true]);
|
||||
$output = Artisan::output();
|
||||
return view('settings/purge')
|
||||
@@ -1015,4 +1100,28 @@ class SettingsController extends Controller
|
||||
public function api() {
|
||||
return view('settings.api');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test the email configuration
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function ajaxTestEmail()
|
||||
{
|
||||
try {
|
||||
Mail::send('emails.test', [], function ($m) {
|
||||
$m->to(config('mail.from.address'), config('mail.from.name'));
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.test_email'));
|
||||
});
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, 'Maiol sent!'));
|
||||
} catch (Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,18 +32,18 @@ class StatuslabelsController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('statuslabels/index', compact('statuslabels'));
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
return view('statuslabels.index');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$statuslabel = Statuslabel::find($id);
|
||||
|
||||
if (isset($statuslabel->id)) {
|
||||
return view('statuslabels/view', compact('statuslabel'));
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
if ($statuslabel = Statuslabel::find($id)) {
|
||||
return view('statuslabels.view')->with('statuslabel', $statuslabel);
|
||||
}
|
||||
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ class StatuslabelsController extends Controller
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
$item = new Statuslabel;
|
||||
$use_statuslabel_type = $item->getStatuslabelType();
|
||||
$statuslabel_types = Helper::statusTypeList();
|
||||
@@ -73,10 +74,11 @@ class StatuslabelsController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
// 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')]);
|
||||
}
|
||||
|
||||
@@ -91,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'));
|
||||
@@ -101,35 +103,6 @@ class StatuslabelsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($statusLabel->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$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.
|
||||
*
|
||||
@@ -138,6 +111,7 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function edit($statuslabelId = null)
|
||||
{
|
||||
$this->authorize('update', Statuslabel::class);
|
||||
// Check if the Statuslabel exists
|
||||
if (is_null($item = Statuslabel::find($statuslabelId))) {
|
||||
// Redirect to the blogs management page
|
||||
@@ -160,13 +134,14 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $statuslabelId = null)
|
||||
{
|
||||
$this->authorize('update', Statuslabel::class);
|
||||
// Check if the Statuslabel exists
|
||||
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
|
||||
// Redirect to the blogs management page
|
||||
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')]);
|
||||
}
|
||||
|
||||
@@ -180,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?
|
||||
@@ -198,19 +174,18 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function destroy($statuslabelId)
|
||||
{
|
||||
$this->authorize('delete', Statuslabel::class);
|
||||
// Check if the Statuslabel exists
|
||||
if (is_null($statuslabel = Statuslabel::find($statuslabelId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
if ($statuslabel->has_assets() == 0) {
|
||||
// Check that there are no assets associated
|
||||
if ($statuslabel->assets()->count() == 0) {
|
||||
$statuslabel->delete();
|
||||
// Redirect to the statuslabels management page
|
||||
return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.delete.success'));
|
||||
}
|
||||
// Redirect to the asset management page
|
||||
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.assoc_assets'));
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user