Compare commits
3054 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a7a10455ae | ||
|
|
bd02b9ed62 | ||
|
|
16f57e16cb | ||
|
|
af6f208c43 | ||
|
|
52270fa4db | ||
|
|
bf3731d65c | ||
|
|
233ebf06ee | ||
|
|
e27f6a483d | ||
|
|
19670f9dd8 | ||
|
|
1448229cd2 | ||
|
|
4721cab928 | ||
|
|
08f3e78d26 | ||
|
|
62227ec27d | ||
|
|
10711245ba | ||
|
|
29a7c8577d | ||
|
|
dfb1ff81e6 | ||
|
|
021e723acf | ||
|
|
14c0c314aa | ||
|
|
d23ea70b08 | ||
|
|
1b047c768b | ||
|
|
e6323e0a1b | ||
|
|
73ce5f98bb | ||
|
|
a37cb616eb | ||
|
|
659d953f3f | ||
|
|
c1dcc22217 | ||
|
|
6a67426140 | ||
|
|
4ba474cf73 | ||
|
|
fb6caa35ff | ||
|
|
a5870c888e | ||
|
|
f35f8477d3 | ||
|
|
d0637d38f3 | ||
|
|
7141968d64 | ||
|
|
0f7b7d8e6a | ||
|
|
ca78b3ed7c | ||
|
|
2d2cae10b9 | ||
|
|
5347b19910 | ||
|
|
5e9331f5ae | ||
|
|
6e30fa6922 | ||
|
|
58b3f0519d | ||
|
|
f119c69698 | ||
|
|
57f4c986af | ||
|
|
2958630923 | ||
|
|
72dacda4f9 | ||
|
|
9c2b986bb0 | ||
|
|
06c5bce3c7 | ||
|
|
a0cbca85bf | ||
|
|
9bda62d295 | ||
|
|
1d7e243d0a | ||
|
|
63bc2ec09f | ||
|
|
d5cadeab1a | ||
|
|
31516abef9 | ||
|
|
d2535582f3 | ||
|
|
eaaea303f4 | ||
|
|
8c5312b931 | ||
|
|
4ef6e292d1 | ||
|
|
6310670835 | ||
|
|
15bb30acd6 | ||
|
|
148d41d8dc | ||
|
|
71c1c74164 | ||
|
|
9c02526a37 | ||
|
|
25dc26aac3 | ||
|
|
afc763ebac | ||
|
|
6a73ec6537 | ||
|
|
481143b891 | ||
|
|
cef67695cd | ||
|
|
4576cb6f56 | ||
|
|
56f88d2c22 | ||
|
|
c1d1cb8122 | ||
|
|
ac8f46c93c | ||
|
|
101a09b421 | ||
|
|
cb1e3b7978 | ||
|
|
de18e2a887 | ||
|
|
241bf0cd4b | ||
|
|
7da4fe4524 | ||
|
|
9bfd345774 | ||
|
|
df87c82ddc | ||
|
|
3e8b7d9c94 | ||
|
|
7adfab9d9f | ||
|
|
7c4ee54f8b | ||
|
|
03f9d01aab | ||
|
|
5b420fb4b9 | ||
|
|
a2d63dd3e4 | ||
|
|
0408509fdc | ||
|
|
5e47c18d68 | ||
|
|
b3293a7a5f | ||
|
|
4e5cf531f7 | ||
|
|
f0bbe38c71 | ||
|
|
ccb7556281 | ||
|
|
ee7348d0d5 | ||
|
|
7d6bf4ac24 | ||
|
|
cb903ab9fd | ||
|
|
66d8308163 | ||
|
|
847262e989 | ||
|
|
59006964d1 | ||
|
|
ac29b142dc | ||
|
|
978a906513 | ||
|
|
dfa0714d44 | ||
|
|
0fbf9236f4 | ||
|
|
c64ca76b3d | ||
|
|
73052d3fc1 | ||
|
|
ae0b639d0c | ||
|
|
5cc84ca137 | ||
|
|
29f1cf2b48 | ||
|
|
7510f9f128 | ||
|
|
7c6143bbb8 | ||
|
|
346156bae1 | ||
|
|
3eea1a23f8 | ||
|
|
1fda4593c1 | ||
|
|
6b5a0983ee | ||
|
|
ad12a5ce11 | ||
|
|
ec09ea53db | ||
|
|
5abe72ce02 | ||
|
|
3e3276334b | ||
|
|
b13e04095e | ||
|
|
3cfcc43efa | ||
|
|
f432f98e12 | ||
|
|
f56862c684 | ||
|
|
9bcfe0748b | ||
|
|
de5e7bd9ba | ||
|
|
635299cd74 | ||
|
|
20376daec4 | ||
|
|
50e1570543 | ||
|
|
eb94c4333d | ||
|
|
a49fd16916 | ||
|
|
0cc9c214aa | ||
|
|
c48bcb7ed1 | ||
|
|
09de201027 | ||
|
|
6ce2127d75 | ||
|
|
e79260a0d4 | ||
|
|
932e25ca9b | ||
|
|
b02148f59e | ||
|
|
6046063666 | ||
|
|
fd57617c8e | ||
|
|
44569558e9 | ||
|
|
d2f6c8af11 | ||
|
|
814bf18a4b | ||
|
|
33557f3792 | ||
|
|
c2927c4a2e | ||
|
|
ab2b2f3043 | ||
|
|
0e598702f8 | ||
|
|
9894edb008 | ||
|
|
c2e8803d1e | ||
|
|
7e1f7297b3 | ||
|
|
3401f3dfb9 | ||
|
|
31cabaa4db | ||
|
|
06c04bf271 | ||
|
|
4c39be1f3c | ||
|
|
c8c279588f | ||
|
|
8aa920ca3a | ||
|
|
0242283a11 | ||
|
|
9e0c5e50b6 | ||
|
|
95d1612234 | ||
|
|
61c6160b98 | ||
|
|
d12159c042 | ||
|
|
e009bb956d | ||
|
|
25e99194ce | ||
|
|
274f3511f5 | ||
|
|
5b9bcd8fa2 | ||
|
|
4bde058192 | ||
|
|
ffba9789b7 | ||
|
|
1052be670d | ||
|
|
770092f23f | ||
|
|
99f65cbf69 | ||
|
|
5d4920c741 | ||
|
|
54279f22a3 | ||
|
|
dfea47a272 | ||
|
|
f0d78091d2 | ||
|
|
966a736602 | ||
|
|
30a9704625 | ||
|
|
30487bfee7 | ||
|
|
3046d7d33c | ||
|
|
0aa76bd946 | ||
|
|
26d7fffae8 | ||
|
|
14f3868b46 | ||
|
|
a5cd4a0a22 | ||
|
|
ad816264e9 | ||
|
|
c2494fe0e5 | ||
|
|
02edb74cdf | ||
|
|
408bb6476f | ||
|
|
ee5b5acffb | ||
|
|
e387673e74 | ||
|
|
17ce4ea08b | ||
|
|
0bc709206f | ||
|
|
e9dd119b9c | ||
|
|
269997a1fb | ||
|
|
041b794e1f | ||
|
|
da2d9b2374 | ||
|
|
a59db7062d | ||
|
|
58b2834c54 | ||
|
|
2aaa635345 | ||
|
|
8269aec71a | ||
|
|
d62896f945 | ||
|
|
a717ca683c | ||
|
|
d7827fedd9 | ||
|
|
fbcc40a145 | ||
|
|
12a7309ecd | ||
|
|
9caf045930 | ||
|
|
1c056d1a59 | ||
|
|
240a07b108 | ||
|
|
c977dda5bb | ||
|
|
03411a3ae8 | ||
|
|
01cc00c832 | ||
|
|
9338b37b74 | ||
|
|
9cd2881ad0 | ||
|
|
64b483b686 | ||
|
|
1f7266274d | ||
|
|
4e5b2d1fe9 | ||
|
|
ada2ac06f2 | ||
|
|
9d948e8d23 | ||
|
|
4062228092 | ||
|
|
3e67cdc501 | ||
|
|
be42a0add1 | ||
|
|
6cadde23d7 | ||
|
|
a6c1f7b30b | ||
|
|
1b5b0e9d19 | ||
|
|
2a226fd3f9 | ||
|
|
043182593d | ||
|
|
08dc07b46b | ||
|
|
9a9b6ae228 | ||
|
|
2d3ea4eec6 | ||
|
|
7c8dc9fe2d | ||
|
|
74aaadcdc5 | ||
|
|
0238dd59a3 | ||
|
|
461ca53289 | ||
|
|
ccc0063556 | ||
|
|
66145a625c | ||
|
|
cc5192c91e | ||
|
|
de5334e525 | ||
|
|
b797604627 | ||
|
|
293981148c | ||
|
|
66eaff739a | ||
|
|
8b5cb6e8c2 | ||
|
|
7b4b2d79cf | ||
|
|
c3af13298e | ||
|
|
312fd27d11 | ||
|
|
1b55d38c63 | ||
|
|
1992906790 | ||
|
|
b0fc2489f6 | ||
|
|
b13726880e | ||
|
|
25dca80644 | ||
|
|
748aba3c48 | ||
|
|
c9484fa30b | ||
|
|
d950ddbda7 | ||
|
|
fd0165b4b8 | ||
|
|
fd0da6ecf0 | ||
|
|
7b1265cb89 | ||
|
|
92052442e3 | ||
|
|
71b668e1a3 | ||
|
|
3c167c9d33 | ||
|
|
9f87f72c96 | ||
|
|
5e0cc93ba2 | ||
|
|
3724442318 | ||
|
|
610a773620 | ||
|
|
a1c7d2922e | ||
|
|
8ab29c628f | ||
|
|
4ada5eaa94 | ||
|
|
5499735f3a | ||
|
|
40e11b6661 | ||
|
|
b1fa25b73e | ||
|
|
452c52a3ab | ||
|
|
76926c97c1 | ||
|
|
6775c39a5e | ||
|
|
ce856bdb0a | ||
|
|
52f24f7587 | ||
|
|
fba7f1ccf6 | ||
|
|
ed46dee646 | ||
|
|
332076bbe5 | ||
|
|
9d45375b8a | ||
|
|
d3b07411da | ||
|
|
d17d3a2ef0 | ||
|
|
dd8511c66a | ||
|
|
f57ebc664e | ||
|
|
f285bd3c29 | ||
|
|
aeca549bab | ||
|
|
e0d2cbef20 | ||
|
|
96e8109d01 | ||
|
|
b223630f72 | ||
|
|
e9c39add4f | ||
|
|
7ec1724308 | ||
|
|
811ddd2f67 | ||
|
|
cefb20d46f | ||
|
|
bb323db685 | ||
|
|
eb3aa99e4f | ||
|
|
a7592de304 | ||
|
|
64f17cecbf | ||
|
|
e2f2f9e154 | ||
|
|
8caf6623f1 | ||
|
|
6c623866c0 | ||
|
|
659e60fd12 | ||
|
|
ae329f4160 | ||
|
|
e314de3594 | ||
|
|
b0479923b1 | ||
|
|
e3a5accace | ||
|
|
b81dd18576 | ||
|
|
dac4b58892 | ||
|
|
71fd430f8e | ||
|
|
5265821bcc | ||
|
|
cb494a74ca | ||
|
|
c526ffbf68 | ||
|
|
6f5fe83a91 | ||
|
|
0c43e64160 | ||
|
|
708385e23a | ||
|
|
0ab37af7ab | ||
|
|
891660d4ea | ||
|
|
dcfc434075 | ||
|
|
0c8308f5a4 | ||
|
|
1bd798c4d2 | ||
|
|
23be188a49 | ||
|
|
fdd88af44a | ||
|
|
5a3816c907 | ||
|
|
c8796cf045 | ||
|
|
f4095c6dd0 | ||
|
|
ee2c67a65f | ||
|
|
5614578710 | ||
|
|
07882110fa | ||
|
|
7a05467dbf | ||
|
|
b7843ea565 | ||
|
|
4f9a253103 | ||
|
|
322817b8b1 | ||
|
|
82dc06ae19 | ||
|
|
38372f2b37 | ||
|
|
e9b056f66c | ||
|
|
f6be7caaeb | ||
|
|
d7f6d8e997 | ||
|
|
841e3efe96 | ||
|
|
8ba19aa855 | ||
|
|
5ad9e8585d | ||
|
|
68e831d555 | ||
|
|
6fbf45554b | ||
|
|
07f8c9dc5e | ||
|
|
e088eea2f7 | ||
|
|
f4cdeaa956 | ||
|
|
59a4e161ff | ||
|
|
6d53a5f58d | ||
|
|
54a2c46101 | ||
|
|
a7132fcdd7 | ||
|
|
7305169b79 | ||
|
|
dcd8e800b1 | ||
|
|
9c637ba62c | ||
|
|
08ef78356d | ||
|
|
ef42bcc223 | ||
|
|
120c08b3a3 | ||
|
|
e32d2f98b5 | ||
|
|
aa354e883e | ||
|
|
89ba221765 | ||
|
|
c7acbc0eee | ||
|
|
961c019554 | ||
|
|
b2e122c1e9 | ||
|
|
5663429280 | ||
|
|
47ac59abef | ||
|
|
1bb89db451 | ||
|
|
7ee172888d | ||
|
|
45ac1de6bb | ||
|
|
f45a3fedca | ||
|
|
99cc8293ef | ||
|
|
e03ebc3fd0 | ||
|
|
0469a44582 | ||
|
|
6f269e18fe | ||
|
|
c5dd33f213 | ||
|
|
c8c5c05351 | ||
|
|
c3cc2c924f | ||
|
|
70761b8157 | ||
|
|
8dbd9fb299 | ||
|
|
00aa06882c | ||
|
|
e475ccc1d4 | ||
|
|
542adfa926 | ||
|
|
36ad82a041 | ||
|
|
79197a719e | ||
|
|
742e3d044e | ||
|
|
a4697f046e | ||
|
|
2c3b9426fe | ||
|
|
ae6828a0fa | ||
|
|
2a4bf65883 | ||
|
|
9027bd1d9d | ||
|
|
f079be5e2f | ||
|
|
343da2968a | ||
|
|
35705813b3 | ||
|
|
5e3daee212 | ||
|
|
25d7345bbd | ||
|
|
76f5b6c0f0 | ||
|
|
9da84a1d4d | ||
|
|
2523072b1d | ||
|
|
f38ef8db7f | ||
|
|
d2b79ce39e | ||
|
|
6b47b0480e | ||
|
|
a916cbb0d0 | ||
|
|
5f7c7503ec | ||
|
|
e6b88fa565 | ||
|
|
db1730ae06 | ||
|
|
1366d0bfb9 | ||
|
|
cf4698d2ed | ||
|
|
8875ce7243 | ||
|
|
07f3ae5e0b | ||
|
|
cbca7e45c3 | ||
|
|
9fccec661b | ||
|
|
bf837af73a | ||
|
|
bd8fc5a07f | ||
|
|
564a9f0ed7 | ||
|
|
ded648f293 | ||
|
|
0b156098df | ||
|
|
3e4fe838f0 | ||
|
|
b3f6e4e47e | ||
|
|
68f3c1727c | ||
|
|
79e9072c0a | ||
|
|
619a1dc63f | ||
|
|
82b072ed44 | ||
|
|
839a053077 | ||
|
|
6fb7f0d7b6 | ||
|
|
2e3f18d5f2 | ||
|
|
7c4ccaf75c | ||
|
|
482cdaadcd | ||
|
|
323899034e | ||
|
|
79bfc1c246 | ||
|
|
8f7e1b0e81 | ||
|
|
2e27456444 | ||
|
|
52140929c4 | ||
|
|
4dd732a6eb | ||
|
|
b7d0432aa6 | ||
|
|
dfd275467e | ||
|
|
e6a9657da1 | ||
|
|
c836c64b0c | ||
|
|
847c604d73 | ||
|
|
67d00ec521 | ||
|
|
edfab0111b | ||
|
|
daa9ccfcf8 | ||
|
|
1aeea54105 | ||
|
|
510fb59a56 | ||
|
|
c355ff8fb7 | ||
|
|
8c6aa6d900 | ||
|
|
04a79f8ad3 | ||
|
|
114e064326 | ||
|
|
5854c62cd4 | ||
|
|
1a5899e276 | ||
|
|
5959308d6e | ||
|
|
f586304086 | ||
|
|
39707943f6 | ||
|
|
e58100252b | ||
|
|
5bd6869be5 | ||
|
|
4c2bba8734 | ||
|
|
4e81b97145 | ||
|
|
a680541fe8 | ||
|
|
793d9e9250 | ||
|
|
b51b51b949 | ||
|
|
23b1a4ca34 | ||
|
|
5b450c5978 | ||
|
|
875d0dd343 | ||
|
|
119e0772c1 | ||
|
|
b16ff263ec | ||
|
|
bb2b6cb642 | ||
|
|
d799ab2a9c | ||
|
|
c511073bc2 | ||
|
|
5803af7409 | ||
|
|
caf2f648b1 | ||
|
|
c132036f5c | ||
|
|
1682867d02 | ||
|
|
da66eecd74 | ||
|
|
73f5457b1b | ||
|
|
f1fd152513 | ||
|
|
bf99534f8d | ||
|
|
ad510cecd5 | ||
|
|
4af2a8eb35 | ||
|
|
fee60ec8dc | ||
|
|
01ae8802c1 | ||
|
|
318ac2be9a | ||
|
|
8dfc44d4fd | ||
|
|
69b014a342 | ||
|
|
5d8faa934b | ||
|
|
878f4f868d | ||
|
|
9af5600c69 | ||
|
|
2e274b5f21 | ||
|
|
97dd5a58fb | ||
|
|
51fccbdda4 | ||
|
|
2b4443f810 | ||
|
|
a9950392c4 | ||
|
|
6a2fd74219 | ||
|
|
097096aa24 | ||
|
|
4f1669189e | ||
|
|
3c53ff9329 | ||
|
|
b5424462c9 | ||
|
|
dc38e575d9 | ||
|
|
161bd7acce | ||
|
|
c6c3ddba3e | ||
|
|
ce2babcafb | ||
|
|
c0c02eebd2 | ||
|
|
9ccaad8b9c | ||
|
|
9e7f998df5 | ||
|
|
e7f7d739ed | ||
|
|
ad9470b6f8 | ||
|
|
265a896211 | ||
|
|
f7e4fca70d | ||
|
|
381526e488 | ||
|
|
7cf42ecf9e | ||
|
|
8c20c2bd39 | ||
|
|
904be03ffd | ||
|
|
ebbc2a60c3 | ||
|
|
53ba2bbb4e | ||
|
|
2c6f323820 | ||
|
|
f520eaa6ce | ||
|
|
fde46251de | ||
|
|
4f73a13c6b | ||
|
|
edf36cd850 | ||
|
|
a710c98b50 | ||
|
|
8fe8ff71e8 | ||
|
|
a18e90c22a | ||
|
|
d9fe39c00e | ||
|
|
9975d55249 | ||
|
|
c67b44b3d5 | ||
|
|
eb3767890e | ||
|
|
74c5c55838 | ||
|
|
eea90dda78 | ||
|
|
959fafe04b | ||
|
|
2892e32095 | ||
|
|
8cce1a3882 | ||
|
|
86198badbb | ||
|
|
2986747fd7 | ||
|
|
72ec362f72 | ||
|
|
4cf01e4f48 | ||
|
|
dd6a477355 | ||
|
|
6d40a98a16 | ||
|
|
584e2ba618 | ||
|
|
79f3ca3074 | ||
|
|
1751a8c0e0 | ||
|
|
d37b9fe169 | ||
|
|
8b0066cbad | ||
|
|
98e0ecbd88 | ||
|
|
2c0df6159f | ||
|
|
9cae6066a0 | ||
|
|
f1c4aed5b5 | ||
|
|
78f2b30b31 | ||
|
|
1e2a23e955 | ||
|
|
a69090c91c | ||
|
|
85d5b75de6 | ||
|
|
cddd3cdb81 | ||
|
|
bb4c443cd9 | ||
|
|
729b709de2 | ||
|
|
f1592aa63f | ||
|
|
cdb6349035 | ||
|
|
6c1a827ed0 | ||
|
|
f9bed9264f | ||
|
|
ca490fd129 | ||
|
|
93719b2214 | ||
|
|
0c3840503e | ||
|
|
498dc9da43 | ||
|
|
7dbbe862ce | ||
|
|
d19cc9d9c1 | ||
|
|
b7d3b65bae | ||
|
|
3c8910eeab | ||
|
|
18e2db24bd | ||
|
|
f1a83bd007 | ||
|
|
12c5ca67ff | ||
|
|
00f7cc028b | ||
|
|
bf919e133c | ||
|
|
541a5e6776 | ||
|
|
464c524375 | ||
|
|
7086ac8a8b | ||
|
|
7642d17fb8 | ||
|
|
a80af854ba | ||
|
|
99d837fa50 | ||
|
|
8d50bb19af | ||
|
|
ec7e89ecb8 | ||
|
|
377cb8e5d1 | ||
|
|
c7003897ae | ||
|
|
231f4dce07 | ||
|
|
5e9e6da577 | ||
|
|
0e1f5c173c | ||
|
|
34ab78ef99 | ||
|
|
11810ae9ad | ||
|
|
5d3568136e | ||
|
|
9ac9c6f1bc | ||
|
|
863735a6e5 | ||
|
|
d02a7064e3 | ||
|
|
b4c6d0c897 | ||
|
|
b0f84fa82b | ||
|
|
19313e4b83 | ||
|
|
5e9c69711b | ||
|
|
cbff2ec57d | ||
|
|
a688822899 | ||
|
|
481722507d | ||
|
|
5ba2ec881c | ||
|
|
a9bf34cf61 | ||
|
|
855d775c41 | ||
|
|
dc47a0fad6 | ||
|
|
de869c7ed0 | ||
|
|
ec88d54a5b | ||
|
|
2a7469d898 | ||
|
|
b1a80f8ed8 | ||
|
|
7350514a0d | ||
|
|
af211c1203 | ||
|
|
ed7e967f4a | ||
|
|
80da0f801c | ||
|
|
5f49e7c1a7 | ||
|
|
75d35273c9 | ||
|
|
04bb180a80 | ||
|
|
8f12486f6c | ||
|
|
f13719bf32 | ||
|
|
c907414e71 | ||
|
|
945d910ca2 | ||
|
|
e8310c8f3c | ||
|
|
e4e4e6ae47 | ||
|
|
afacee90ae | ||
|
|
ebe3f58805 | ||
|
|
88faefe1ed | ||
|
|
8f0ab1e719 | ||
|
|
b019e6e950 | ||
|
|
eea779c16d | ||
|
|
b1dcc208e9 | ||
|
|
d0f062458e | ||
|
|
6d2a37fa80 | ||
|
|
9d555f5436 | ||
|
|
0f9e39e4cd | ||
|
|
df4c7e32b1 | ||
|
|
ad4bf83aaa | ||
|
|
19cb428fb0 | ||
|
|
b7555e780c | ||
|
|
ea3d45882d | ||
|
|
bebb653a70 | ||
|
|
a2bf685901 | ||
|
|
5d14acaac4 | ||
|
|
47cf734f72 | ||
|
|
3161420eb3 | ||
|
|
297034c037 | ||
|
|
db7b1b586c | ||
|
|
f6ccbfc9b0 | ||
|
|
eac025263b | ||
|
|
8a4a4264cd | ||
|
|
fd594ed608 | ||
|
|
7b1d2ee050 | ||
|
|
b3f70a046f | ||
|
|
39b4725114 | ||
|
|
f642013774 | ||
|
|
b462888850 | ||
|
|
9bc10edc8d | ||
|
|
e284e9900a | ||
|
|
ba71122449 | ||
|
|
d39b7e19eb | ||
|
|
e54036991c | ||
|
|
acd8eafc8c | ||
|
|
2879114fe9 | ||
|
|
b968839a1d | ||
|
|
89edda2c23 | ||
|
|
73eee1b7b1 | ||
|
|
213a234bef | ||
|
|
14ddcc56a2 | ||
|
|
edca6ad19d | ||
|
|
ae540af2a9 | ||
|
|
34f427920b | ||
|
|
e7a243f8bc | ||
|
|
66f724cd35 | ||
|
|
b423fa3bd4 | ||
|
|
295744be64 | ||
|
|
fbbf5e77b4 | ||
|
|
c7e1b426e7 | ||
|
|
bc5fcf8736 | ||
|
|
06d66f6e94 | ||
|
|
f9a501d774 | ||
|
|
f54fcb88e2 | ||
|
|
58b1ac5f0f | ||
|
|
d2f84a2329 | ||
|
|
4f35c8af3e | ||
|
|
a35323131f | ||
|
|
fbd6dcbfbb | ||
|
|
9c41881d99 | ||
|
|
f52680b971 | ||
|
|
e66e830b44 | ||
|
|
6d7c226acd | ||
|
|
ca2fa3dab9 | ||
|
|
267f0bdfea | ||
|
|
2f43f78e62 | ||
|
|
b11a951e95 | ||
|
|
ed325d2fe5 | ||
|
|
3c6010679a | ||
|
|
d9d048f90d | ||
|
|
b7a650e986 | ||
|
|
534e40feb8 | ||
|
|
a283b13069 | ||
|
|
41b864cc49 | ||
|
|
03874a5481 | ||
|
|
7d2d6a3c1b | ||
|
|
29b5678a8d | ||
|
|
93542f3478 | ||
|
|
8f2019c62e | ||
|
|
fddcc0fefd | ||
|
|
15373d5f4a | ||
|
|
704ee59541 | ||
|
|
5a848566f9 | ||
|
|
268308004c | ||
|
|
0cf4014b75 | ||
|
|
95f2d94e01 | ||
|
|
c506f30562 | ||
|
|
2c695cf7e5 | ||
|
|
74696d5ded | ||
|
|
7a124e85d6 | ||
|
|
b8d81c0734 | ||
|
|
900e6e2419 | ||
|
|
ff2c489af4 | ||
|
|
55d0f8c0df | ||
|
|
aefbb76592 | ||
|
|
73d45e9a14 | ||
|
|
15bab7fa9e | ||
|
|
8f31ee318e | ||
|
|
c7167fc73c | ||
|
|
9e4a391b8b | ||
|
|
1d2489cf9c | ||
|
|
1364494e92 | ||
|
|
dbf3a074f7 | ||
|
|
4940bf05f0 | ||
|
|
c08fe196c4 | ||
|
|
93a087b29a | ||
|
|
4590054c75 | ||
|
|
80e047f679 | ||
|
|
86926675ce | ||
|
|
41f58efb70 | ||
|
|
e2d5401f3a | ||
|
|
9bbfb64f17 | ||
|
|
84154d936a | ||
|
|
57d0eba58f | ||
|
|
a02f406fa6 | ||
|
|
19fcda5c25 | ||
|
|
b5ddd9ab0a | ||
|
|
d14ed4a73c | ||
|
|
33ffc58ffe | ||
|
|
11d86d83b6 | ||
|
|
c99f9e321f | ||
|
|
b7bbc44052 | ||
|
|
734e87f85f | ||
|
|
f371c5fd62 | ||
|
|
4b3edbd2f5 | ||
|
|
34a9564f30 | ||
|
|
3060de8e0c | ||
|
|
1f3106b9da | ||
|
|
44683c784f | ||
|
|
b4bed2a11f | ||
|
|
57d2b88900 | ||
|
|
9fd3a9a82d | ||
|
|
a6e6991a2d | ||
|
|
93ba0717d8 | ||
|
|
208ccab4c1 | ||
|
|
a8839e0ef4 | ||
|
|
3e4152c966 | ||
|
|
1bf34d73f5 | ||
|
|
d1e360d64d | ||
|
|
3b5b19848c | ||
|
|
6ce20c32b1 | ||
|
|
816d2fd095 | ||
|
|
f999c797e5 | ||
|
|
6351746c62 | ||
|
|
3a6bbcc615 | ||
|
|
8a782bf34a | ||
|
|
6029bea2de | ||
|
|
4ad9c3623e | ||
|
|
4c5fce847b | ||
|
|
0581499029 | ||
|
|
16bd78fb3f | ||
|
|
199fdf39ec | ||
|
|
221cf1f9c8 | ||
|
|
ce8eda1325 | ||
|
|
51ceaedfaf | ||
|
|
62dd474d44 | ||
|
|
aa2d3cf026 | ||
|
|
fa974b93c0 | ||
|
|
13b51bc934 | ||
|
|
7ccd71978c | ||
|
|
8cc695b65f | ||
|
|
165487ac92 | ||
|
|
719463ef54 | ||
|
|
fd805bde50 | ||
|
|
55415e8f56 | ||
|
|
d262aec4c3 | ||
|
|
13cf11368f | ||
|
|
cafafe851c | ||
|
|
06af9311fc | ||
|
|
fd450e2773 | ||
|
|
57374955a8 | ||
|
|
8857faee65 | ||
|
|
02c1a45025 | ||
|
|
d1fecb6128 | ||
|
|
40615f9504 | ||
|
|
61543f3a04 | ||
|
|
297b9d25f0 | ||
|
|
94a6b555a8 | ||
|
|
0cf5ad120b | ||
|
|
93dd27339b | ||
|
|
42db9563ae | ||
|
|
fc18aa7f6d | ||
|
|
d6b7036518 | ||
|
|
2fe984013b | ||
|
|
323c3807fa | ||
|
|
cd8c585377 | ||
|
|
ae2cb5fe68 | ||
|
|
c7e98366be | ||
|
|
84e1b531fd | ||
|
|
9ea05bacf3 | ||
|
|
76344a8c9b | ||
|
|
4751bcd002 | ||
|
|
f832b15cf3 | ||
|
|
e685e0f019 | ||
|
|
040774d646 | ||
|
|
37b4b29653 | ||
|
|
01f9deb5a6 | ||
|
|
d6b41759f0 | ||
|
|
e8945ad85e | ||
|
|
406b828b4e | ||
|
|
c308fbce0d | ||
|
|
3e4be6671e | ||
|
|
2bb94e6806 | ||
|
|
f182a7db2a | ||
|
|
aab0933856 | ||
|
|
b7a2c4c26d | ||
|
|
c6ab34faee | ||
|
|
6c8e9327c1 | ||
|
|
8ad509d7f1 | ||
|
|
e5286229e0 | ||
|
|
cf2b57cb15 | ||
|
|
4603000d12 | ||
|
|
816affc83b | ||
|
|
ad1bf86a08 | ||
|
|
bea1a93e9b | ||
|
|
1ab414453f | ||
|
|
eb9207d0fe | ||
|
|
0c5d3d1c74 | ||
|
|
456e4a633e | ||
|
|
3106c336bb | ||
|
|
d46e77c037 | ||
|
|
a6b975b168 | ||
|
|
8faffb1889 | ||
|
|
b17e380aae | ||
|
|
9b167d87d2 | ||
|
|
7f65c6e472 | ||
|
|
2f03c5ddb0 | ||
|
|
e67b3e474f | ||
|
|
a369a111e0 | ||
|
|
112ee3c8ff | ||
|
|
a8e09728a5 | ||
|
|
899493e4d5 | ||
|
|
30bc3cbe9b | ||
|
|
030207d626 | ||
|
|
fb0cce0640 | ||
|
|
4753d70946 | ||
|
|
38eb910999 | ||
|
|
9840683c97 | ||
|
|
43619fa3c9 | ||
|
|
4f2439ae1c | ||
|
|
a80ece9b84 | ||
|
|
25f60264bd | ||
|
|
97a1a166f0 | ||
|
|
91fe136fd3 | ||
|
|
37c847ea08 | ||
|
|
37f93a2861 | ||
|
|
7c774352e5 | ||
|
|
863e200430 | ||
|
|
8e5977ad84 | ||
|
|
a8d5d4a446 | ||
|
|
68ac4abe2c | ||
|
|
69031d4215 | ||
|
|
433adb1dcb | ||
|
|
5cd7e84d98 | ||
|
|
6f55db1251 | ||
|
|
44312b0026 | ||
|
|
c5b24c83df | ||
|
|
f6e567d5d4 | ||
|
|
b83f73f7d6 | ||
|
|
8ca5c6c25e | ||
|
|
f0dd4c5a97 | ||
|
|
65db55908a | ||
|
|
93ba90e837 | ||
|
|
dd28c5709e | ||
|
|
fc70d79a17 | ||
|
|
42fe481f71 | ||
|
|
95f1a98b96 | ||
|
|
6f1e0d6d9f | ||
|
|
ae66bba0f1 | ||
|
|
32c5a258a7 | ||
|
|
e7ac860f77 | ||
|
|
899a991a3b | ||
|
|
f26d86dff2 | ||
|
|
84317f7f50 | ||
|
|
65016a2383 | ||
|
|
eb48e5ed1c | ||
|
|
dbcb2ccb46 | ||
|
|
927a12f78d | ||
|
|
dd52b4828c | ||
|
|
6273e313bc | ||
|
|
2afcfcc87c |
1673
.all-contributorsrc
Normal file
1673
.all-contributorsrc
Normal file
File diff suppressed because it is too large
Load Diff
39
.env.example
39
.env.example
@@ -7,29 +7,30 @@ 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=true
|
||||
DB_SSL=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 +44,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 +52,6 @@ MAIL_REPLYTO_NAME='Snipe-IT'
|
||||
# --------------------------------------------
|
||||
IMAGE_LIB=gd
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SESSION SETTINGS
|
||||
# --------------------------------------------
|
||||
@@ -62,6 +62,14 @@ 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
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: CACHE SETTINGS
|
||||
@@ -69,7 +77,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,7 +110,9 @@ 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# --------------------------------------------
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_KEY=ChangeMe
|
||||
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
|
||||
APP_URL=http://localhost:8000
|
||||
APP_TIMEZONE='US/Pacific'
|
||||
APP_LOCALE=en
|
||||
@@ -21,7 +21,7 @@ DB_PASSWORD=null
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_DRIVER=log
|
||||
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=YOURUSERNAME
|
||||
@@ -69,3 +69,4 @@ SECURE_COOKIES=false
|
||||
# OPTIONAL: APP LOG FORMAT
|
||||
# --------------------------------------------
|
||||
APP_LOG=single
|
||||
APP_LOG_LEVEL=debug
|
||||
|
||||
@@ -15,13 +15,13 @@ FILESYSTEM_DISK=local
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=snipeit_unit
|
||||
DB_USERNAME=travis
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=null
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: OUTGOING MAIL SERVER SETTINGS
|
||||
# --------------------------------------------
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_DRIVER=log
|
||||
MAIL_HOST=email-smtp.us-west-2.amazonaws.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=YOURUSERNAME
|
||||
|
||||
15
.env.tests
15
.env.tests
@@ -1,9 +1,22 @@
|
||||
APP_ENV=local
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://snipe-it.localapp
|
||||
DB_CONNECTION=mysql
|
||||
DB_DEFAULT=mysql
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=snipeittests
|
||||
DB_USERNAME=snipeit
|
||||
DB_PASSWORD=snipe
|
||||
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
|
||||
|
||||
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
|
||||
22
.github/CODEOWNERS
vendored
Normal file
22
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Code ownership for pull request reviews, per
|
||||
# the feature detailed here:
|
||||
|
||||
# https://github.com/blog/2392-introducing-code-owners
|
||||
# https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
* @snipe
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# So if a pull request only touches javascript files, only these owners
|
||||
# will be requested to review.
|
||||
|
||||
# For example:
|
||||
# *.js @octocat @github/js
|
||||
|
||||
|
||||
app/Importer/* @dmeltzer
|
||||
app/Http/Controllers/CustomFields* @uberbrady
|
||||
app/Http/Controllers/Api/CustomFields* @uberbrady
|
||||
resources/views/custom_fields/* @uberbrady
|
||||
docker/* @uberbrady
|
||||
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
|
||||
13
.gitignore
vendored
13
.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
|
||||
@@ -38,3 +46,8 @@ tests/_data/scenarios
|
||||
tests/_output/*
|
||||
tests/_support/_generated/*
|
||||
/npm-debug.log
|
||||
/storage/oauth-private.key
|
||||
/storage/oauth-public.key
|
||||
|
||||
*.cache
|
||||
/public/storage
|
||||
|
||||
52
.travis.yml
52
.travis.yml
@@ -1,4 +1,7 @@
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token:
|
||||
secure: "C/bUAEpwfZB82dkzI2Nxx3PW5w/BzbKkSyCkp6YjT046jD2/QKvz6ngCFlt3tAWV11TXWFI6D8DzkMmdWOrQl3SGlPZXRD8QOvCiz0HiGMDvlxjAaPaQecGaQZdx/H4m6xTUXRNUVaYmxlMgkkFCWhAp+HZDs0iyOEVamp0Jszg="
|
||||
hosts:
|
||||
- localhost
|
||||
sudo: false
|
||||
@@ -6,26 +9,39 @@ sudo: false
|
||||
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
|
||||
language: php
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
# 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 10
|
||||
- mysql -e "create database IF NOT EXISTS snipeit_unit;" -utravis
|
||||
- sleep 4
|
||||
- mysql -e 'CREATE DATABASE snipeit_unit;'
|
||||
- mysql -e 'CREATE USER "travis'@'localhost";'
|
||||
- mysql -e 'GRANT ALL PRIVILEGES ON * . * TO "travis'@'localhost";'
|
||||
- mysql -e 'FLUSH PRIVILEGES;'
|
||||
- composer self-update
|
||||
- composer install -n --prefer-source
|
||||
- cp .env.testing-ci .env
|
||||
- chmod -R 777 storage
|
||||
- php artisan migrate --database=mysql --force
|
||||
- php artisan migrate --env=testing-ci --database=mysql --force
|
||||
- ./vendor/bin/codecept build
|
||||
- php artisan key:generate
|
||||
- php artisan db:seed --database=mysql --force
|
||||
- php artisan snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
|
||||
- php artisan serve --port=8000 --host=localhost &
|
||||
- php artisan --env=testing-ci key:generate
|
||||
- php artisan --env=testing-ci snipeit:travisci-install
|
||||
- php artisan --env=testing-ci db:seed --database=mysql --force
|
||||
- php artisan --env=testing-ci snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
|
||||
- php artisan --env=testing-ci passport:install
|
||||
- php artisan serve --env=testing-ci --port=8000 --host=localhost &
|
||||
- sleep 5
|
||||
- pip install --user codecov
|
||||
- sleep 5
|
||||
@@ -34,16 +50,24 @@ before_script:
|
||||
|
||||
# omitting "script:" will default to phpunit
|
||||
# use the $DB env variable to determine the phpunit.xml to use
|
||||
# script: ./vendor/bin/codecept run --env testing-ci - broken :(
|
||||
script: ./vendor/bin/codecept run unit --env testing-ci
|
||||
#script: ./vendor/bin/codecept run
|
||||
# script: ./vendor/bin/codecept run --env testing-ci
|
||||
script:
|
||||
- ./vendor/bin/codecept run unit
|
||||
# - ./vendor/bin/codecept run acceptance --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
|
||||
|
||||
after_failure:
|
||||
- cat tests/_output/AccessoriesCept.fail.html
|
||||
- cat tests/_output/*.fail.html
|
||||
- curl http://localhost:8000/login
|
||||
- cat storage/logs/laravel.log
|
||||
|
||||
# configure notifications (email, IRC, campfire etc)
|
||||
notifications:
|
||||
|
||||
@@ -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.
|
||||
|
||||
6
CONTRIBUTING.md
Normal file
6
CONTRIBUTING.md
Normal file
@@ -0,0 +1,6 @@
|
||||
### 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.
|
||||
58
Dockerfile
58
Dockerfile
@@ -1,27 +1,38 @@
|
||||
FROM ubuntu:trusty
|
||||
MAINTAINER Brady Wetherington <uberbrady@gmail.com>
|
||||
FROM ubuntu:xenial
|
||||
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-php5 \
|
||||
php5-curl \
|
||||
php5-ldap \
|
||||
php5-mysql \
|
||||
php5-mcrypt \
|
||||
php5-gd \
|
||||
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
|
||||
mysql-client \
|
||||
supervisor \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
RUN php5enmod mcrypt
|
||||
RUN php5enmod gd
|
||||
RUN phpenmod mcrypt
|
||||
RUN phpenmod gd
|
||||
RUN phpenmod bcmath
|
||||
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php5/apache2/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php5/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 --uid 1000 --gid 50 docker
|
||||
RUN useradd -m --uid 1000 --gid 50 docker
|
||||
|
||||
RUN echo export APACHE_RUN_USER=docker >> /etc/apache2/envvars
|
||||
RUN echo export APACHE_RUN_GROUP=staff >> /etc/apache2/envvars
|
||||
@@ -55,9 +66,13 @@ 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" \
|
||||
&& mkdir -p "/var/lib/snipeit/data/uploads/{assets,avatars,barcodes,models,suppliers}" \
|
||||
&& 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 ###################
|
||||
|
||||
@@ -65,7 +80,9 @@ RUN \
|
||||
RUN cd /tmp;curl -sS https://getcomposer.org/installer | php;mv /tmp/composer.phar /usr/local/bin/composer
|
||||
|
||||
# Get dependencies
|
||||
RUN cd /var/www/html;composer install
|
||||
USER docker
|
||||
RUN cd /var/www/html;composer install && rm -rf /home/docker/.composer/cache
|
||||
USER root
|
||||
|
||||
############### APPLICATION INSTALL/INIT #################
|
||||
|
||||
@@ -82,10 +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
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/startup.sh"]
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
79
README.md
79
README.md
@@ -1,15 +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)
|
||||
[](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.2](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.
|
||||
|
||||
-----
|
||||
|
||||
@@ -21,12 +22,14 @@ If you're having trouble with the installation, please check the [Common Issues]
|
||||
|
||||
-----
|
||||
### User's Manual
|
||||
For help using Snipe-IT, check out the [user's manual](https://snipe-it-manual.readme.io/docs).
|
||||
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
|
||||
|
||||
-----
|
||||
### Bug Reports & Feature Requests
|
||||
|
||||
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open and closed) to see if your question hasn't already been answered before opening a new issue.
|
||||
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
|
||||
|
||||
**PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
|
||||
|
||||
-----
|
||||
|
||||
@@ -47,9 +50,71 @@ 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:
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/5547470?v=3" width="110px;"/><br /><sub>Hereward</sub>](https://github.com/thehereward)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thehereward "Code") | [<img src="https://avatars0.githubusercontent.com/u/5802977?v=3" width="110px;"/><br /><sub>swoopdk</sub>](https://github.com/swoopdk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=swoopdk "Code") | [<img src="https://avatars1.githubusercontent.com/u/3470403?v=3" width="110px;"/><br /><sub>Abdullah Alansari</sub>](https://linkedin.com/in/ahimta)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Ahimta "Code") | [<img src="https://avatars0.githubusercontent.com/u/796443?v=3" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MicaelRodrigues "Code") | [<img src="https://avatars0.githubusercontent.com/u/614564?v=3" width="110px;"/><br /><sub>Patrick Gallagher</sub>](http://macadmincorner.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=patgmac "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/7165922?v=3" width="110px;"/><br /><sub>Miliamber</sub>](https://github.com/Miliamber)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Miliamber "Code") | [<img src="https://avatars3.githubusercontent.com/u/861766?v=3" width="110px;"/><br /><sub>hawk554</sub>](https://github.com/hawk554)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hawk554 "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/1695622?v=3" width="110px;"/><br /><sub>Justin Kerr</sub>](http://jbirdkerr.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbirdkerr "Code") | [<img src="https://avatars3.githubusercontent.com/u/11426176?v=3" width="110px;"/><br /><sub>Ira W. Snyder</sub>](http://www.irasnyder.com/devel/)<br />[📖](https://github.com/snipe/snipe-it/commits?author=irasnyd "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2475759?v=3" width="110px;"/><br /><sub>Aladin Alaily</sub>](https://github.com/aalaily)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aalaily "Code") | [<img src="https://avatars0.githubusercontent.com/u/10247644?v=3" width="110px;"/><br /><sub>Chase Hansen</sub>](https://github.com/kobie-chasehansen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kobie-chasehansen "Code") [💬](#question-kobie-chasehansen "Answering Questions") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Akobie-chasehansen "Bug reports") | [<img src="https://avatars2.githubusercontent.com/u/13545400?v=3" width="110px;"/><br /><sub>IDM Helpdesk</sub>](https://github.com/IDM-Helpdesk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=IDM-Helpdesk "Code") | [<img src="https://avatars2.githubusercontent.com/u/614439?v=3" width="110px;"/><br /><sub>Kai</sub>](http://balticer.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balticer "Code") | [<img src="https://avatars1.githubusercontent.com/u/8762511?v=3" width="110px;"/><br /><sub>Michael Daniels</sub>](http://www.michaeldaniels.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mdaniels5757 "Code") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/1532660?v=3" width="110px;"/><br /><sub>Tom Castleman</sub>](http://tomcastleman.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tomcastleman "Code") | [<img src="https://avatars3.githubusercontent.com/u/10723243?v=3" width="110px;"/><br /><sub>Daniel Nemanic</sub>](https://github.com/DanielNemanic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielNemanic "Code") | [<img src="https://avatars0.githubusercontent.com/u/150648?v=3" width="110px;"/><br /><sub>SouthWolf</sub>](https://github.com/southwolf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=southwolf "Code") | [<img src="https://avatars2.githubusercontent.com/u/131616?v=3" width="110px;"/><br /><sub>Ivar Nesje</sub>](https://github.com/ivarne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ivarne "Code") | [<img src="https://avatars1.githubusercontent.com/u/62333?v=3" width="110px;"/><br /><sub>Jérémy Benoist</sub>](http://www.j0k3r.net)<br />[📖](https://github.com/snipe/snipe-it/commits?author=j0k3r "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/724344?v=3" width="110px;"/><br /><sub>Chris Leathley</sub>](https://github.com/cleathley)<br />[🚇](#infra-cleathley "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars0.githubusercontent.com/u/972498?v=3" width="110px;"/><br /><sub>splaer</sub>](https://github.com/splaer)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asplaer "Bug reports") [💻](https://github.com/snipe/snipe-it/commits?author=splaer "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/967362?v=3" width="110px;"/><br /><sub>Joe Ferguson</sub>](http://www.joeferguson.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=svpernova09 "Code") | [<img src="https://avatars3.githubusercontent.com/u/6108682?v=3" width="110px;"/><br /><sub>diwanicki</sub>](https://github.com/diwanicki)<br />[💻](https://github.com/snipe/snipe-it/commits?author=diwanicki "Code") [📖](https://github.com/snipe/snipe-it/commits?author=diwanicki "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/2527115?v=3" width="110px;"/><br /><sub>Lee Thoong Ching</sub>](https://github.com/pakkua80)<br />[📖](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Documentation") [💻](https://github.com/snipe/snipe-it/commits?author=pakkua80 "Code") | [<img src="https://avatars1.githubusercontent.com/u/461491?v=3" width="110px;"/><br /><sub>Marek Šuppa</sub>](http://shu.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrshu "Code") | [<img src="https://avatars1.githubusercontent.com/u/8693762?v=3" width="110px;"/><br /><sub>Juan J. Martinez</sub>](https://github.com/mizar1616)<br />[🌍](#translation-mizar1616 "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1458388?v=3" width="110px;"/><br /><sub>R Ryan Dial</sub>](https://github.com/rrdial)<br />[🌍](#translation-rrdial "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2871745?v=3" width="110px;"/><br /><sub>Andrej Manduch</sub>](https://github.com/burlito)<br />[📖](https://github.com/snipe/snipe-it/commits?author=burlito "Documentation") |
|
||||
| [<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://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") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
-----
|
||||
|
||||
### 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.
|
||||
|
||||
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
|
||||
@@ -1,463 +0,0 @@
|
||||
<?php
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Location;
|
||||
use App\Models\Category;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Manufacturer;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use League\Csv\Reader;
|
||||
|
||||
class AssetImportCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'snipeit:asset-import';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import Assets from CSV';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
|
||||
if (!$this->option('testrun')=='true') {
|
||||
$this->comment('======= Importing Assets from '.$filename.' =========');
|
||||
} else {
|
||||
$this->comment('====== TEST ONLY Asset Import for '.$filename.' ====');
|
||||
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
|
||||
}
|
||||
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath($this->argument('filename'));
|
||||
$csv->setNewline("\r\n");
|
||||
$csv->setOffset(1);
|
||||
$duplicates = '';
|
||||
|
||||
// Loop through the records
|
||||
$nbInsert = $csv->each(function ($row) use ($duplicates) {
|
||||
$status_id = 1;
|
||||
|
||||
// Let's just map some of these entries to more user friendly words
|
||||
|
||||
// User's name
|
||||
if (array_key_exists('0', $row)) {
|
||||
$user_name = trim($row[0]);
|
||||
} else {
|
||||
$user_name = '';
|
||||
}
|
||||
|
||||
// User's email
|
||||
if (array_key_exists('1', $row)) {
|
||||
$user_email = trim($row[1]);
|
||||
} else {
|
||||
$user_email = '';
|
||||
}
|
||||
|
||||
// User's email
|
||||
if (array_key_exists('2', $row)) {
|
||||
$user_username = trim($row[2]);
|
||||
} else {
|
||||
$user_username = '';
|
||||
}
|
||||
|
||||
// Asset Name
|
||||
if (array_key_exists('3', $row)) {
|
||||
$user_asset_asset_name = trim($row[3]);
|
||||
} else {
|
||||
$user_asset_asset_name = '';
|
||||
}
|
||||
|
||||
// Asset Category
|
||||
if (array_key_exists('4', $row)) {
|
||||
$user_asset_category = trim($row[4]);
|
||||
} else {
|
||||
$user_asset_category = '';
|
||||
}
|
||||
|
||||
// Asset Name
|
||||
if (array_key_exists('5', $row)) {
|
||||
$user_asset_name = trim($row[5]);
|
||||
} else {
|
||||
$user_asset_name = '';
|
||||
}
|
||||
|
||||
// Asset Manufacturer
|
||||
if (array_key_exists('6', $row)) {
|
||||
$user_asset_mfgr = trim($row[6]);
|
||||
} else {
|
||||
$user_asset_mfgr = '';
|
||||
}
|
||||
|
||||
// Asset model number
|
||||
if (array_key_exists('7', $row)) {
|
||||
$user_asset_modelno = trim($row[7]);
|
||||
} else {
|
||||
$user_asset_modelno = '';
|
||||
}
|
||||
|
||||
// Asset serial number
|
||||
if (array_key_exists('8', $row)) {
|
||||
$user_asset_serial = trim($row[8]);
|
||||
} else {
|
||||
$user_asset_serial = '';
|
||||
}
|
||||
|
||||
// Asset tag
|
||||
if (array_key_exists('9', $row)) {
|
||||
$user_asset_tag = trim($row[9]);
|
||||
} else {
|
||||
$user_asset_tag = '';
|
||||
}
|
||||
|
||||
// Asset location
|
||||
if (array_key_exists('10', $row)) {
|
||||
$user_asset_location = trim($row[10]);
|
||||
} else {
|
||||
$user_asset_location = '';
|
||||
}
|
||||
|
||||
// Asset notes
|
||||
if (array_key_exists('11', $row)) {
|
||||
$user_asset_notes = trim($row[11]);
|
||||
} else {
|
||||
$user_asset_notes = '';
|
||||
}
|
||||
|
||||
// Asset purchase date
|
||||
if (array_key_exists('12', $row)) {
|
||||
if ($row[12]!='') {
|
||||
$user_asset_purchase_date = date("Y-m-d 00:00:01", strtotime($row[12]));
|
||||
} else {
|
||||
$user_asset_purchase_date = '';
|
||||
}
|
||||
} else {
|
||||
$user_asset_purchase_date = '';
|
||||
}
|
||||
|
||||
// Asset purchase cost
|
||||
if (array_key_exists('13', $row)) {
|
||||
if ($row[13]!='') {
|
||||
$user_asset_purchase_cost = trim($row[13]);
|
||||
} else {
|
||||
$user_asset_purchase_cost = '';
|
||||
}
|
||||
} else {
|
||||
$user_asset_purchase_cost = '';
|
||||
}
|
||||
|
||||
// Asset Company Name
|
||||
if (array_key_exists('14', $row)) {
|
||||
if ($row[14]!='') {
|
||||
$user_asset_company_name = trim($row[14]);
|
||||
} else {
|
||||
$user_asset_company_name= '';
|
||||
}
|
||||
} else {
|
||||
$user_asset_company_name = '';
|
||||
}
|
||||
|
||||
|
||||
// A number was given instead of a name
|
||||
if (is_numeric($user_name)) {
|
||||
$this->comment('User '.$user_name.' is not a name - assume this user already exists');
|
||||
$user_username = '';
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
|
||||
// No name was given
|
||||
} elseif ($user_name=='') {
|
||||
$this->comment('No user data provided - skipping user creation, just adding asset');
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
//$user_username = '';
|
||||
|
||||
} else {
|
||||
$user_email_array = User::generateFormattedNameFromFullName($this->option('email_format'), $user_name);
|
||||
$first_name = $user_email_array['first_name'];
|
||||
$last_name = $user_email_array['last_name'];
|
||||
|
||||
if ($user_email=='') {
|
||||
$user_email = $user_email_array['username'].'@'.config('app.domain');
|
||||
}
|
||||
|
||||
if ($user_username=='') {
|
||||
if ($this->option('username_format')=='email') {
|
||||
$user_username = $user_email;
|
||||
} else {
|
||||
$user_name_array = User::generateFormattedNameFromFullName($this->option('username_format'), $user_name);
|
||||
$user_username = $user_name_array['username'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->comment('Full Name: '.$user_name);
|
||||
$this->comment('First Name: '.$first_name);
|
||||
$this->comment('Last Name: '.$last_name);
|
||||
$this->comment('Username: '.$user_username);
|
||||
$this->comment('Email: '.$user_email);
|
||||
$this->comment('Category Name: '.$user_asset_category);
|
||||
$this->comment('Item: '.$user_asset_name);
|
||||
$this->comment('Manufacturer ID: '.$user_asset_mfgr);
|
||||
$this->comment('Model No: '.$user_asset_modelno);
|
||||
$this->comment('Serial No: '.$user_asset_serial);
|
||||
$this->comment('Asset Tag: '.$user_asset_tag);
|
||||
$this->comment('Location: '.$user_asset_location);
|
||||
$this->comment('Purchase Date: '.$user_asset_purchase_date);
|
||||
$this->comment('Purchase Cost: '.$user_asset_purchase_cost);
|
||||
$this->comment('Notes: '.$user_asset_notes);
|
||||
$this->comment('Company Name: '.$user_asset_company_name);
|
||||
|
||||
$this->comment('------------- Action Summary ----------------');
|
||||
|
||||
if ($user_username!='') {
|
||||
if ($user = User::MatchEmailOrUsername($user_username, $user_email)
|
||||
->whereNotNull('username')->first()) {
|
||||
$this->comment('User '.$user_username.' already exists');
|
||||
} else {
|
||||
$user = new \App\Models\User;
|
||||
$password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
|
||||
$user->first_name = $first_name;
|
||||
$user->last_name = $last_name;
|
||||
$user->username = $user_username;
|
||||
$user->email = $user_email;
|
||||
$user->permissions = '{user":1}';
|
||||
$user->password = bcrypt($password);
|
||||
$user->activated = 1;
|
||||
if ($user->save()) {
|
||||
$this->comment('User '.$first_name.' created');
|
||||
} else {
|
||||
$this->error('ERROR CREATING User '.$first_name.' '.$last_name);
|
||||
$this->error($user->getErrors());
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$user = new User;
|
||||
}
|
||||
|
||||
// Check for the location match and create it if it doesn't exist
|
||||
if ($location = Location::where('name', e($user_asset_location))->first()) {
|
||||
$this->comment('Location '.$user_asset_location.' already exists');
|
||||
} else {
|
||||
|
||||
$location = new Location();
|
||||
|
||||
if ($user_asset_location!='') {
|
||||
$location->name = e($user_asset_location);
|
||||
$location->address = '';
|
||||
$location->city = '';
|
||||
$location->state = '';
|
||||
$location->country = '';
|
||||
$location->user_id = 1;
|
||||
|
||||
if (!$this->option('testrun')=='true') {
|
||||
|
||||
if ($location->save()) {
|
||||
$this->comment('Location '.$user_asset_location.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Location '.$user_asset_location.' was NOT created');
|
||||
$this->error($location->getErrors());
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->comment('Location '.$user_asset_location.' was (not) created - test run only');
|
||||
}
|
||||
} else {
|
||||
$this->comment('No location given, so none created.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (e($user_asset_category)=='') {
|
||||
$category_name = 'Unnamed Category';
|
||||
} else {
|
||||
$category_name = e($user_asset_category);
|
||||
}
|
||||
|
||||
// Check for the category match and create it if it doesn't exist
|
||||
if ($category = Category::where('name', e($category_name))->where('category_type', 'asset')->first()) {
|
||||
$this->comment('Category '.$category_name.' already exists');
|
||||
|
||||
} else {
|
||||
$category = new Category();
|
||||
$category->name = e($category_name);
|
||||
$category->category_type = 'asset';
|
||||
$category->user_id = 1;
|
||||
|
||||
if ($category->save()) {
|
||||
$this->comment('Category '.$user_asset_category.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Category '.$user_asset_category.' was NOT created');
|
||||
$this->error($category->getErrors());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check for the manufacturer match and create it if it doesn't exist
|
||||
if ($manufacturer = Manufacturer::where('name', e($user_asset_mfgr))->first()) {
|
||||
$this->comment('Manufacturer '.$user_asset_mfgr.' already exists');
|
||||
} else {
|
||||
$manufacturer = new Manufacturer();
|
||||
$manufacturer->name = e($user_asset_mfgr);
|
||||
$manufacturer->user_id = 1;
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
$this->comment('Manufacturer '.$user_asset_mfgr.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Manufacturer '.$user_asset_mfgr.' was NOT created: '. $manufacturer->getErrors()->first());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check for the asset model match and create it if it doesn't exist
|
||||
if ($asset_model = AssetModel::where('name', e($user_asset_name))->where('modelno', e($user_asset_modelno))->where('category_id', $category->id)->where('manufacturer_id', $manufacturer->id)->first()) {
|
||||
$this->comment('The Asset Model '.$user_asset_name.' with model number '.$user_asset_modelno.' already exists');
|
||||
} else {
|
||||
$asset_model = new AssetModel();
|
||||
$asset_model->name = e($user_asset_name);
|
||||
$asset_model->manufacturer_id = $manufacturer->id;
|
||||
$asset_model->modelno = e($user_asset_modelno);
|
||||
$asset_model->category_id = $category->id;
|
||||
$asset_model->user_id = 1;
|
||||
|
||||
if ($asset_model->save()) {
|
||||
$this->comment('Asset Model '.$user_asset_name.' with model number '.$user_asset_modelno.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Asset Model '.$user_asset_name.' was NOT created: '.$asset_model->getErrors()->first());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check for the asset company match and create it if it doesn't exist
|
||||
if ($user_asset_company_name!='') {
|
||||
if ($company = Company::where('name', e($user_asset_company_name))->first()) {
|
||||
$this->comment('Company '.$user_asset_company_name.' already exists');
|
||||
} else {
|
||||
$company = new Company();
|
||||
$company->name = e($user_asset_company_name);
|
||||
|
||||
if ($company->save()) {
|
||||
$this->comment('Company '.$user_asset_company_name.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Company '.$user_asset_company_name.' was NOT created: '.$company->getErrors()->first());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$company = new Company();
|
||||
}
|
||||
|
||||
// Check for the asset match and create it if it doesn't exist
|
||||
if ($asset = Asset::where('asset_tag', e($user_asset_tag))->first()) {
|
||||
$this->comment('The Asset with asset tag '.$user_asset_tag.' already exists');
|
||||
} else {
|
||||
$asset = new Asset();
|
||||
$asset->name = e($user_asset_asset_name);
|
||||
if ($user_asset_purchase_date!='') {
|
||||
$asset->purchase_date = $user_asset_purchase_date;
|
||||
} else {
|
||||
$asset->purchase_date = null;
|
||||
}
|
||||
if ($user_asset_purchase_cost!='') {
|
||||
$asset->purchase_cost = ParseFloat(e($user_asset_purchase_cost));
|
||||
} else {
|
||||
$asset->purchase_cost = 0.00;
|
||||
}
|
||||
$asset->serial = e($user_asset_serial);
|
||||
$asset->asset_tag = e($user_asset_tag);
|
||||
$asset->model_id = $asset_model->id;
|
||||
$asset->assigned_to = $user->id;
|
||||
$asset->rtd_location_id = $location->id;
|
||||
$asset->user_id = 1;
|
||||
$asset->status_id = $status_id;
|
||||
$asset->company_id = $company->id;
|
||||
if ($user_asset_purchase_date!='') {
|
||||
$asset->purchase_date = $user_asset_purchase_date;
|
||||
} else {
|
||||
$asset->purchase_date = null;
|
||||
}
|
||||
$asset->notes = e($user_asset_notes);
|
||||
|
||||
if ($asset->save()) {
|
||||
$this->comment('Asset '.$user_asset_name.' with serial number '.$user_asset_serial.' was created');
|
||||
} else {
|
||||
$this->error('Something went wrong! Asset '.$user_asset_name.' was NOT created: '.$asset->getErrors()->first());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->comment('=====================================');
|
||||
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
array('filename', InputArgument::REQUIRED, 'File for the CSV import.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
array('email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null),
|
||||
array('username_format', null, InputOption::VALUE_REQUIRED, 'The format of the username that should be generated. Options are firstname.lastname, firstname, filastname, email', null),
|
||||
array('testrun', null, InputOption::VALUE_REQUIRED, 'Test the output without writing to the database or not.', null),
|
||||
);
|
||||
}
|
||||
}
|
||||
85
app/Console/Commands/FixDoubleEscape.php
Normal file
85
app/Console/Commands/FixDoubleEscape.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class FixDoubleEscape extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:unescape';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This should be run to fix some double-escaping issues from earlier versions of Snipe-IT.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$tables = [
|
||||
'\App\Models\Asset' => ['name'],
|
||||
'\App\Models\License' => ['name'],
|
||||
'\App\Models\Consumable' => ['name'],
|
||||
'\App\Models\Accessory' => ['name'],
|
||||
'\App\Models\Component' => ['name'],
|
||||
'\App\Models\Company' => ['name'],
|
||||
'\App\Models\Manufacturer' => ['name'],
|
||||
'\App\Models\Supplier' => ['name'],
|
||||
'\App\Models\Statuslabel' => ['name'],
|
||||
'\App\Models\Depreciation' => ['name'],
|
||||
'\App\Models\AssetModel' => ['name'],
|
||||
'\App\Models\Group' => ['name'],
|
||||
'\App\Models\Department' => ['name'],
|
||||
'\App\Models\Location' => ['name'],
|
||||
'\App\Models\User' => ['first_name', 'last_name'],
|
||||
];
|
||||
|
||||
$count = array();
|
||||
|
||||
|
||||
|
||||
foreach ($tables as $classname => $fields) {
|
||||
$count[$classname] = array();
|
||||
$count[$classname]['classname'] = 0;
|
||||
|
||||
foreach($fields as $field) {
|
||||
|
||||
$count[$classname]['classname']++;
|
||||
$count[$classname][$field] = 0;
|
||||
|
||||
foreach($classname::where("$field",'LIKE','%&%')->get() as $row) {
|
||||
$this->info('Updating '.$field.' for '.$classname);
|
||||
$row->{$field} = html_entity_decode($row->{$field});
|
||||
$row->save();
|
||||
$count[$classname][$field]++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Update complete');
|
||||
|
||||
}
|
||||
}
|
||||
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() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
157
app/Console/Commands/LdapSync.php
Normal file → Executable file
157
app/Console/Commands/LdapSync.php
Normal file → Executable file
@@ -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.
|
||||
@@ -55,41 +55,107 @@ 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();
|
||||
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 [];
|
||||
}
|
||||
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = NULL;
|
||||
|
||||
if ($this->option('location')!='') {
|
||||
$location = Location::where('name','=',$this->option('location'))->first();
|
||||
$location = Location::where('name', '=', $this->option('location'))->first();
|
||||
LOG::debug('Location name '.$this->option('location').' passed');
|
||||
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
} elseif ($this->option('location_id')!='') {
|
||||
$location = Location::where('id','=',$this->option('location_id'))->first();
|
||||
$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 = new Location;
|
||||
}
|
||||
|
||||
if (!isset($location)) {
|
||||
LOG::debug('That location is invalid, so no location will be assigned.');
|
||||
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
/* 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"]);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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") {
|
||||
|
||||
@@ -99,30 +165,52 @@ class LdapSync extends Command
|
||||
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
|
||||
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
|
||||
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
|
||||
$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;
|
||||
|
||||
if ($location) {
|
||||
$user->location_id = e($location->id);
|
||||
// Sync activated state for Active Directory.
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
$enabled_accounts = [
|
||||
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224', '4260352'
|
||||
];
|
||||
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
// If we're not using AD, and there isn't an activated flag set, activate all users
|
||||
elseif (empty($ldap_result_active_flag)) {
|
||||
$user->activated = 1;
|
||||
}
|
||||
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
} elseif ((isset($location)) && (!empty($location))) {
|
||||
|
||||
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 = '';
|
||||
@@ -146,19 +234,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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
<?php
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use League\Csv\Reader;
|
||||
use App\Models\User;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
|
||||
class LicenseImportCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'snipeit:license-import';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import Licenses from CSV';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
|
||||
if (!$this->option('testrun')=='true') {
|
||||
$this->comment('======= Importing Licenses from '.$filename.' =========');
|
||||
} else {
|
||||
$this->comment('====== TEST ONLY License Import for '.$filename.' ====');
|
||||
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
|
||||
}
|
||||
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath($this->argument('filename'));
|
||||
$csv->setNewline("\r\n");
|
||||
$csv->setOffset(1);
|
||||
$duplicates = '';
|
||||
|
||||
// Loop through the records
|
||||
$nbInsert = $csv->each(function ($row) use ($duplicates) {
|
||||
$status_id = 1;
|
||||
|
||||
// Let's just map some of these entries to more user friendly words
|
||||
|
||||
if (array_key_exists('0', $row)) {
|
||||
$user_name = trim($row[0]);
|
||||
} else {
|
||||
$user_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('1', $row)) {
|
||||
$user_email = trim($row[1]);
|
||||
} else {
|
||||
$user_email = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('2', $row)) {
|
||||
$user_username = trim($row[2]);
|
||||
} else {
|
||||
$user_username = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('3', $row)) {
|
||||
$user_license_name = trim($row[3]);
|
||||
} else {
|
||||
$user_license_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('4', $row)) {
|
||||
$user_license_serial = trim($row[4]);
|
||||
} else {
|
||||
$user_license_serial = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('5', $row)) {
|
||||
$user_licensed_to_name = trim($row[5]);
|
||||
} else {
|
||||
$user_licensed_to_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('6', $row)) {
|
||||
$user_licensed_to_email = trim($row[6]);
|
||||
} else {
|
||||
$user_licensed_to_email = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('7', $row)) {
|
||||
$user_license_seats = trim($row[7]);
|
||||
} else {
|
||||
$user_license_seats = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('8', $row)) {
|
||||
$user_license_reassignable = trim($row[8]);
|
||||
if ($user_license_reassignable!='') {
|
||||
if ((strtolower($user_license_reassignable)=='yes') || (strtolower($user_license_reassignable)=='true') || ($user_license_reassignable=='1')) {
|
||||
$user_license_reassignable = 1;
|
||||
}
|
||||
} else {
|
||||
$user_license_reassignable = 0;
|
||||
}
|
||||
} else {
|
||||
$user_license_reassignable = 0;
|
||||
}
|
||||
|
||||
if (array_key_exists('9', $row)) {
|
||||
$user_license_supplier = trim($row[9]);
|
||||
} else {
|
||||
$user_license_supplier = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('10', $row)) {
|
||||
$user_license_maintained = trim($row[10]);
|
||||
|
||||
if ($user_license_maintained!='') {
|
||||
if ((strtolower($user_license_maintained)=='yes') || (strtolower($user_license_maintained)=='true') || ($user_license_maintained=='1')) {
|
||||
$user_license_maintained = 1;
|
||||
}
|
||||
} else {
|
||||
$user_license_maintained = 0;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$user_license_maintained = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('11', $row)) {
|
||||
$user_license_notes = trim($row[11]);
|
||||
} else {
|
||||
$user_license_notes = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('12', $row)) {
|
||||
if ($row[12]!='') {
|
||||
$user_license_purchase_date = date("Y-m-d 00:00:01", strtotime($row[12]));
|
||||
} else {
|
||||
$user_license_purchase_date = '';
|
||||
}
|
||||
} else {
|
||||
$user_license_purchase_date = 0;
|
||||
}
|
||||
|
||||
// A number was given instead of a name
|
||||
if (is_numeric($user_name)) {
|
||||
$this->comment('User '.$user_name.' is not a name - assume this user already exists');
|
||||
$user_username = '';
|
||||
// No name was given
|
||||
|
||||
} elseif ($user_name=='') {
|
||||
$this->comment('No user data provided - skipping user creation, just adding license');
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
$user_username = '';
|
||||
|
||||
} else {
|
||||
|
||||
$name = explode(" ", $user_name);
|
||||
$first_name = $name[0];
|
||||
$email_last_name = '';
|
||||
$email_prefix = $first_name;
|
||||
|
||||
if (!array_key_exists(1, $name)) {
|
||||
$last_name='';
|
||||
$email_last_name = $last_name;
|
||||
$email_prefix = $first_name;
|
||||
} else {
|
||||
$last_name = str_replace($first_name, '', $user_name);
|
||||
|
||||
if ($this->option('email_format')=='filastname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name[0].$email_last_name;
|
||||
|
||||
} elseif ($this->option('email_format')=='firstname.lastname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name.'.'.$email_last_name;
|
||||
|
||||
} elseif ($this->option('email_format')=='firstname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
$user_username = $email_prefix;
|
||||
|
||||
// Generate an email based on their name if no email address is given
|
||||
if ($user_email=='') {
|
||||
if ($first_name=='Unknown') {
|
||||
$status_id = 7;
|
||||
}
|
||||
$email = strtolower($email_prefix).'@'.$this->option('domain');
|
||||
$user_email = str_replace("'", '', $email);
|
||||
}
|
||||
}
|
||||
|
||||
$this->comment('Full Name: '.$user_name);
|
||||
$this->comment('First Name: '.$first_name);
|
||||
$this->comment('Last Name: '.$last_name);
|
||||
$this->comment('Username: '.$user_username);
|
||||
$this->comment('Email: '.$user_email);
|
||||
$this->comment('License Name: '.$user_license_name);
|
||||
$this->comment('Serial No: '.$user_license_serial);
|
||||
$this->comment('Licensed To Name: '.$user_licensed_to_name);
|
||||
$this->comment('Licensed To Email: '.$user_licensed_to_email);
|
||||
$this->comment('Seats: '.$user_license_seats);
|
||||
$this->comment('Reassignable: '.$user_license_reassignable);
|
||||
$this->comment('Supplier: '.$user_license_supplier);
|
||||
$this->comment('Maintained: '.$user_license_maintained);
|
||||
$this->comment('Notes: '.$user_license_notes);
|
||||
$this->comment('Purchase Date: '.$user_license_purchase_date);
|
||||
|
||||
$this->comment('------------- Action Summary ----------------');
|
||||
|
||||
if ($user_username!='') {
|
||||
if ($user = User::where('username', $user_username)->whereNotNull('username')->first()) {
|
||||
$this->comment('User '.$user_username.' already exists');
|
||||
} else {
|
||||
|
||||
$user = new \App\Models\User;
|
||||
$password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
|
||||
$user->first_name = $first_name;
|
||||
$user->last_name = $last_name;
|
||||
$user->username = $user_username;
|
||||
$user->email = $user_email;
|
||||
$user->permissions = '{user":1}';
|
||||
$user->password = bcrypt($password);
|
||||
$user->activated = 1;
|
||||
if ($user->save()) {
|
||||
$this->comment('User '.$first_name.' created');
|
||||
} else {
|
||||
$this->error('ERROR CREATING User '.$first_name.' '.$last_name);
|
||||
$this->error($user->getErrors());
|
||||
}
|
||||
|
||||
$this->comment('User '.$first_name.' created');
|
||||
}
|
||||
} else {
|
||||
$user = new User;
|
||||
$user->user_id = null;
|
||||
}
|
||||
|
||||
|
||||
// Check for the supplier match and create it if it doesn't exist
|
||||
if ($supplier = Supplier::where('name', $user_license_supplier)->first()) {
|
||||
$this->comment('Supplier '.$user_license_supplier.' already exists');
|
||||
} else {
|
||||
$supplier = new Supplier();
|
||||
$supplier->name = e($user_license_supplier);
|
||||
$supplier->user_id = 1;
|
||||
|
||||
if ($supplier->save()) {
|
||||
$this->comment('Supplier '.$user_license_supplier.' was created');
|
||||
} else {
|
||||
$this->comment('Something went wrong! Supplier '.$user_license_supplier.' was NOT created');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Add the license
|
||||
$license = new License();
|
||||
$license->name = e($user_license_name);
|
||||
if ($user_license_purchase_date!='') {
|
||||
$license->purchase_date = $user_license_purchase_date;
|
||||
} else {
|
||||
$license->purchase_date = null;
|
||||
}
|
||||
$license->serial = e($user_license_serial);
|
||||
$license->seats = e($user_license_seats);
|
||||
$license->supplier_id = $supplier->id;
|
||||
$license->user_id = 1;
|
||||
if ($user_license_purchase_date!='') {
|
||||
$license->purchase_date = $user_license_purchase_date;
|
||||
} else {
|
||||
$license->purchase_date = null;
|
||||
}
|
||||
$license->license_name = $user_licensed_to_name;
|
||||
$license->license_email = $user_licensed_to_email;
|
||||
$license->notes = e($user_license_notes);
|
||||
|
||||
if ($license->save()) {
|
||||
$this->comment('License '.$user_license_name.' with serial number '.$user_license_serial.' was created');
|
||||
|
||||
|
||||
$license_seat_created = 0;
|
||||
|
||||
for ($x = 0; $x < $user_license_seats; $x++) {
|
||||
// Create the license seat entries
|
||||
$license_seat = new LicenseSeat();
|
||||
$license_seat->license_id = $license->id;
|
||||
|
||||
// Only assign the first seat to the user
|
||||
if ($x==0) {
|
||||
$license_seat->assigned_to = $user->id;
|
||||
} else {
|
||||
$license_seat->assigned_to = null;
|
||||
}
|
||||
|
||||
if ($license_seat->save()) {
|
||||
$license_seat_created++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($license_seat_created > 0) {
|
||||
$this->comment($license_seat_created.' seats were created');
|
||||
} else {
|
||||
$this->comment('Something went wrong! NO seats for '.$user_license_name.' were created');
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
$this->comment('Something went wrong! License '.$user_license_name.' was NOT created');
|
||||
}
|
||||
|
||||
|
||||
$this->comment('=====================================');
|
||||
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
array('filename', InputArgument::REQUIRED, 'File for the CSV import.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
array('domain', null, InputOption::VALUE_REQUIRED, 'Email domain for generated email addresses.', null),
|
||||
array('email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null),
|
||||
array('testrun', null, InputOption::VALUE_REQUIRED, 'Test the output without writing to the database or not.', null),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,6 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use DB;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
@@ -12,14 +9,18 @@ use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Department;
|
||||
use App\Models\Depreciation;
|
||||
use App\Models\Group;
|
||||
use App\Models\Import;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class PaveIt extends Command
|
||||
{
|
||||
@@ -63,6 +64,7 @@ class PaveIt extends Command
|
||||
Company::getQuery()->delete();
|
||||
Component::getQuery()->delete();
|
||||
Consumable::getQuery()->delete();
|
||||
Department::getQuery()->delete();
|
||||
Depreciation::getQuery()->delete();
|
||||
License::getQuery()->delete();
|
||||
LicenseSeat::getQuery()->delete();
|
||||
@@ -72,10 +74,12 @@ class PaveIt extends Command
|
||||
Statuslabel::getQuery()->delete();
|
||||
Supplier::getQuery()->delete();
|
||||
Group::getQuery()->delete();
|
||||
Import::getQuery()->delete();
|
||||
|
||||
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');
|
||||
@@ -107,6 +111,7 @@ class PaveIt extends Command
|
||||
\DB::statement('drop table IF EXISTS custom_fields');
|
||||
\DB::statement('drop table IF EXISTS custom_fieldsets');
|
||||
\DB::statement('drop table IF EXISTS depreciations');
|
||||
\DB::statement('drop table IF EXISTS departments');
|
||||
\DB::statement('drop table IF EXISTS groups');
|
||||
\DB::statement('drop table IF EXISTS history');
|
||||
\DB::statement('drop table IF EXISTS components');
|
||||
@@ -117,6 +122,11 @@ class PaveIt extends Command
|
||||
\DB::statement('drop table IF EXISTS manufacturers');
|
||||
\DB::statement('drop table IF EXISTS models');
|
||||
\DB::statement('drop table IF EXISTS migrations');
|
||||
\DB::statement('drop table IF EXISTS oauth_access_tokens');
|
||||
\DB::statement('drop table IF EXISTS oauth_auth_codes');
|
||||
\DB::statement('drop table IF EXISTS oauth_clients');
|
||||
\DB::statement('drop table IF EXISTS oauth_personal_access_clients');
|
||||
\DB::statement('drop table IF EXISTS oauth_refresh_tokens');
|
||||
\DB::statement('drop table IF EXISTS password_resets');
|
||||
\DB::statement('drop table IF EXISTS requested_assets');
|
||||
\DB::statement('drop table IF EXISTS requests');
|
||||
@@ -126,6 +136,7 @@ class PaveIt extends Command
|
||||
\DB::statement('drop table IF EXISTS throttle');
|
||||
\DB::statement('drop table IF EXISTS users_groups');
|
||||
\DB::statement('drop table IF EXISTS users');
|
||||
\DB::statement('drop table IF EXISTS imports');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -65,7 +65,7 @@ class Purge extends Command
|
||||
$asset_maintenances = 0;
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$this->info('- Asset "'.$asset->showAssetName().'" deleted.');
|
||||
$this->info('- Asset "'.$asset->present()->name().'" deleted.');
|
||||
$asset_assoc += $asset->assetlog()->count();
|
||||
$asset->assetlog()->forceDelete();
|
||||
$asset_maintenances += $asset->assetmaintenances()->count();
|
||||
|
||||
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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
167
app/Console/Commands/RecryptFromMcrypt.php
Normal file
167
app/Console/Commands/RecryptFromMcrypt.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Console\Command;
|
||||
use App\LegacyEncrypter\McryptEncrypter;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class RecryptFromMcrypt extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:legacy-recrypt
|
||||
{--force : Force a re-crypt of encrypted data from MCRYPT.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command allows upgrading users to de-encrypt their deprecated mcrypt encrypted fields and re-encrypt them using the current OpenSSL encryption.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
|
||||
// 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) {
|
||||
$this->error('ERROR: You do not have a LEGACY_APP_KEY set in your .env file. Please locate your old APP_KEY and ADD a line to your .env file like: LEGACY_APP_KEY=YOUR_OLD_APP_KEY');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Do some basic legacy app key length checks
|
||||
if (strlen($legacy_key) == 32) {
|
||||
$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 {
|
||||
$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;
|
||||
}
|
||||
|
||||
$this->error('================================!!!! WARNING !!!!================================');
|
||||
$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.");
|
||||
|
||||
$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');
|
||||
|
||||
try {
|
||||
Storage::disk('local')->put($backup_file, 'APP_KEY: '.config('app.key'));
|
||||
Storage::disk('local')->append($backup_file, 'LEGACY_APP_KEY: '.$legacy_key);
|
||||
} catch (\Exception $e) {
|
||||
$this->info('WARNING: Could not backup app keys');
|
||||
}
|
||||
|
||||
|
||||
if ($legacy_cipher){
|
||||
$mcrypter = new McryptEncrypter($legacy_key,$legacy_cipher);
|
||||
}else{
|
||||
$mcrypter = new McryptEncrypter($legacy_key);
|
||||
}
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
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...');
|
||||
|
||||
$query = Asset::withTrashed();
|
||||
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
$this->comment('FIELD TO RECRYPT: '.$custom_field->name .' ('.$custom_field->db_column.')');
|
||||
$query->orWhereNotNull($custom_field->db_column);
|
||||
}
|
||||
|
||||
|
||||
// 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 ($assets as $asset) {
|
||||
foreach ($custom_fields as $encrypted_field) {
|
||||
$columnName = $encrypted_field->db_column;
|
||||
|
||||
// Make sure the value isn't null
|
||||
if ($asset->{$columnName}!='') {
|
||||
// Try to decrypt the payload using the legacy app key
|
||||
try {
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$asset->save();
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
$bar->finish();
|
||||
|
||||
if (count($errors) > 0) {
|
||||
$this->comment("\n\n");
|
||||
$this->error("The decrypter encountered some errors: \n");
|
||||
foreach ($errors as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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->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->showAssetName().'</a></td><td>'.e($asset->asset_tag).'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.e($asset->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->assigneduser ? e($asset->assigneduser->fullName()) : '').'</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="'.config('app.url').'/admin/licenses/'.$license->id.'/view">';
|
||||
$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,90 +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,29 +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\LicenseImportCommand::class,
|
||||
Commands\ObjectImportCommand::class,
|
||||
Commands\Versioning::class,
|
||||
Commands\SystemBackup::class,
|
||||
Commands\DisableLDAP::class,
|
||||
Commands\Purge::class,
|
||||
Commands\LdapSync::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
@@ -37,7 +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;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
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
|
||||
{
|
||||
@@ -17,10 +17,13 @@ class Handler extends ExceptionHandler
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
AuthorizationException::class,
|
||||
HttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
ValidationException::class,
|
||||
\Illuminate\Auth\AuthenticationException::class,
|
||||
\Illuminate\Auth\Access\AuthorizationException::class,
|
||||
\Symfony\Component\HttpKernel\Exception\HttpException::class,
|
||||
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
|
||||
\Illuminate\Session\TokenMismatchException::class,
|
||||
\Illuminate\Validation\ValidationException::class,
|
||||
\Intervention\Image\Exception\NotSupportedException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -28,12 +31,15 @@ class Handler extends ExceptionHandler
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param \Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
parent::report($e);
|
||||
if ($this->shouldReport($exception)) {
|
||||
Log::error($exception);
|
||||
return parent::report($exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,23 +51,74 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
|
||||
|
||||
// CSRF token mismatch error
|
||||
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
|
||||
return redirect()->back()->with('error', trans('general.token_expired'));
|
||||
return redirect()->back()->with('error', trans('general.token_expired'));
|
||||
}
|
||||
|
||||
if ($this->isHttpException($e)) {
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
// Handle Ajax requests that fail because the model doesn't exist
|
||||
if ($request->ajax() || $request->wantsJson()) {
|
||||
|
||||
switch ($statusCode) {
|
||||
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
|
||||
$className = last(explode('\\', $e->getModel()));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $className . ' not found'), 200);
|
||||
}
|
||||
|
||||
case '404':
|
||||
return response()->view('layouts/basic', [
|
||||
'content' => view('errors/404')
|
||||
]);
|
||||
}
|
||||
if ($this->isHttpException($e)) {
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
|
||||
switch ($e->getStatusCode()) {
|
||||
case '404':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
|
||||
case '405':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
|
||||
default:
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), 405);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($this->isHttpException($e) && (isset($statusCode)) && ($statusCode == '404' )) {
|
||||
return response()->view('layouts/basic', [
|
||||
'content' => view('errors/404')
|
||||
],$statusCode);
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an authentication exception into an unauthenticated response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Auth\AuthenticationException $exception
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['error' => 'Unauthorized.'], 401);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Helpers;
|
||||
use DB;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Location;
|
||||
use App\Models\Department;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
@@ -17,6 +18,7 @@ use App\Models\Component;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Crypt;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
|
||||
@@ -31,7 +33,8 @@ class Helper
|
||||
* @since [v2.0]
|
||||
* @return String
|
||||
*/
|
||||
public static function parseEscapedMarkedown($str) {
|
||||
public static function parseEscapedMarkedown($str)
|
||||
{
|
||||
$Parsedown = new \Parsedown();
|
||||
|
||||
if ($str) {
|
||||
@@ -123,77 +126,17 @@ class Helper
|
||||
$LocaleInfo = localeconv();
|
||||
$floatString = str_replace(",", "", $floatString);
|
||||
$floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString);
|
||||
// Strip Currency symbol
|
||||
// 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->displayModelName();
|
||||
}
|
||||
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('0' => trans('general.select_company')) + DB::table('companies')
|
||||
->orderBy('name', 'asc')
|
||||
->pluck('name', 'id');
|
||||
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 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
|
||||
*
|
||||
@@ -203,42 +146,11 @@ class Helper
|
||||
*/
|
||||
public static function statusLabelList()
|
||||
{
|
||||
$statuslabel_list = array('' => trans('general.select_statuslabel')) + Statuslabel::orderBy('name', 'asc')
|
||||
$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
|
||||
*
|
||||
@@ -248,28 +160,15 @@ class Helper
|
||||
*/
|
||||
public static function statusTypeList()
|
||||
{
|
||||
$statuslabel_types = array('' => trans('admin/hardware/form.select_statustype')) + array('undeployable' => trans('admin/hardware/general.undeployable')) + array('pending' => trans('admin/hardware/general.pending')) + array('archived' => trans('admin/hardware/general.archived')) + array('deployable' => trans('admin/hardware/general.deployable'));
|
||||
$statuslabel_types =
|
||||
array('' => trans('admin/hardware/form.select_statustype'))
|
||||
+ array('deployable' => trans('admin/hardware/general.deployable'))
|
||||
+ array('pending' => trans('admin/hardware/general.pending'))
|
||||
+ array('undeployable' => trans('admin/hardware/general.undeployable'))
|
||||
+ array('archived' => trans('admin/hardware/general.archived'));
|
||||
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()
|
||||
->lists('complete_name', 'id')->toArray();
|
||||
|
||||
return $manager_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of depreciations in an array to make a dropdown menu
|
||||
*
|
||||
@@ -293,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()
|
||||
->lists('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('assignedUser', 'model'), 'assets.company_id')->get()->lists('detailed_name', 'id')->toArray();
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of custom fields in an array to make a dropdown menu
|
||||
*
|
||||
@@ -367,9 +225,9 @@ class Helper
|
||||
*/
|
||||
public static function predefined_formats()
|
||||
{
|
||||
$keys=array_keys(CustomField::$PredefinedFormats);
|
||||
$stuff=array_combine($keys, $keys);
|
||||
return $stuff+["" => trans('admin/custom_fields/general.custom_format')];
|
||||
$keys = array_keys(CustomField::$PredefinedFormats);
|
||||
$stuff = array_combine($keys, $keys);
|
||||
return $stuff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -423,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();
|
||||
@@ -435,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;
|
||||
}
|
||||
@@ -444,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++;
|
||||
}
|
||||
@@ -453,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;
|
||||
}
|
||||
@@ -466,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++;
|
||||
}
|
||||
@@ -474,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;
|
||||
}
|
||||
@@ -486,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++;
|
||||
}
|
||||
@@ -553,7 +411,7 @@ class Helper
|
||||
if ($permission[$x]['display'] === true) {
|
||||
|
||||
if ($selected_arr) {
|
||||
if (array_key_exists($permission_name,$selected_arr)) {
|
||||
if (array_key_exists($permission_name, $selected_arr)) {
|
||||
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
|
||||
} else {
|
||||
$permissions_arr[$permission_name] = '0';
|
||||
@@ -584,7 +442,8 @@ class Helper
|
||||
* @since [v3.0]
|
||||
* @return boolean
|
||||
*/
|
||||
public static function checkIfRequired($class, $field) {
|
||||
public static function checkIfRequired($class, $field)
|
||||
{
|
||||
$rules = $class::rules();
|
||||
foreach ($rules as $rule_name => $rule) {
|
||||
if ($rule_name == $field) {
|
||||
@@ -611,28 +470,10 @@ class Helper
|
||||
*/
|
||||
public static function array_smart_fetch(array $array, $key, $default = '')
|
||||
{
|
||||
array_change_key_case($array, CASE_LOWER);
|
||||
array_change_key_case($array, CASE_LOWER);
|
||||
return array_key_exists(strtolower($key), array_change_key_case($array)) ? e(trim($array[ $key ])) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the given key exists in the array, and trim excess white space before returning it
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 3.2
|
||||
* @param $array array
|
||||
* @return string
|
||||
*/
|
||||
public static function getLastDateFromHistoryArray(array $array)
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
// echo '<pre>';
|
||||
// echo 'last:'.$key;
|
||||
// print_r($array);
|
||||
// echo '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gracefully handle decrypting the legacy data (encrypted via mcrypt) and use the new
|
||||
@@ -646,7 +487,8 @@ class Helper
|
||||
* @param String $string
|
||||
* @return string
|
||||
*/
|
||||
public static function gracefulDecrypt(CustomField $field, $string) {
|
||||
public static function gracefulDecrypt(CustomField $field, $string)
|
||||
{
|
||||
|
||||
if ($field->isFieldDecryptable($string)) {
|
||||
|
||||
@@ -663,26 +505,171 @@ class Helper
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip HTML out of returned JSON. This is pretty gross, and I'd like to find a better way
|
||||
* to handle this, but the REST API will solve some of these problems anyway.
|
||||
*
|
||||
* This is not currently used, but will be.
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 3.4
|
||||
* @param $array array
|
||||
* @return Array
|
||||
*/
|
||||
public static function stripTagsFromJSON(Array $array) {
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
$clean_value = strip_tags($value);
|
||||
$clean_array[$key] = $clean_value;
|
||||
|
||||
public static function formatStandardApiResponse($status, $payload = null, $messages = null) {
|
||||
|
||||
$array['status'] = $status;
|
||||
$array['messages'] = $messages;
|
||||
if (($messages) && (is_array($messages)) && (count($messages) > 0)) {
|
||||
$array['messages'] = $messages;
|
||||
}
|
||||
return $clean_array;
|
||||
($payload) ? $array['payload'] = $payload : $array['payload'] = null;
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Possible solution for unicode fieldnames
|
||||
*/
|
||||
public static function make_slug($string) {
|
||||
return preg_replace('/\s+/u', '_', trim($string));
|
||||
}
|
||||
|
||||
|
||||
public static function getFormattedDateObject($date, $type = 'datetime', $array = true) {
|
||||
|
||||
if ($date=='') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$tmp_date = new \Carbon($date);
|
||||
|
||||
if ($type == 'datetime') {
|
||||
$dt['datetime'] = $tmp_date->format('Y-m-d H:i:s');
|
||||
$dt['formatted'] = $tmp_date->format($settings->date_display_format .' '. $settings->time_display_format);
|
||||
} else {
|
||||
$dt['date'] = $tmp_date->format('Y-m-d');
|
||||
$dt['formatted'] = $tmp_date->format($settings->date_display_format);
|
||||
}
|
||||
|
||||
if ($array == 'true') {
|
||||
return $dt;
|
||||
}
|
||||
return $dt['formatted'];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
@@ -14,12 +12,13 @@ use DB;
|
||||
use Gate;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Mail;
|
||||
use Redirect;
|
||||
use Request;
|
||||
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.
|
||||
@@ -38,9 +37,10 @@ class AccessoriesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex(Request $request)
|
||||
public function index(Request $request)
|
||||
{
|
||||
return View::make('accessories/index');
|
||||
$this->authorize('index', Accessory::class);
|
||||
return view('accessories/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -50,15 +50,12 @@ class AccessoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate(Request $request)
|
||||
public function create(Request $request)
|
||||
{
|
||||
// Show the page
|
||||
return View::make('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());
|
||||
$this->authorize('create', Accessory::class);
|
||||
$category_type = 'accessory';
|
||||
return view('accessories/edit')->with('category_type', $category_type)
|
||||
->with('item', new Accessory);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,45 +65,53 @@ class AccessoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize(Accessory::class);
|
||||
// create a new model instance
|
||||
$accessory = new Accessory();
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->name = e(Input::get('name'));
|
||||
$accessory->category_id = e(Input::get('category_id'));
|
||||
$accessory->location_id = e(Input::get('location_id'));
|
||||
$accessory->min_amt = e(Input::get('min_amt'));
|
||||
$accessory->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$accessory->order_number = e(Input::get('order_number'));
|
||||
$accessory->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$accessory->model_number = e(Input::get('model_number'));
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$accessory->purchase_date = null;
|
||||
} else {
|
||||
$accessory->purchase_date = e(Input::get('purchase_date'));
|
||||
}
|
||||
|
||||
if (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$accessory->purchase_cost = null;
|
||||
} else {
|
||||
$accessory->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
|
||||
}
|
||||
|
||||
$accessory->qty = e(Input::get('qty'));
|
||||
$accessory->name = request('name');
|
||||
$accessory->category_id = request('category_id');
|
||||
$accessory->location_id = request('location_id');
|
||||
$accessory->min_amt = request('min_amt');
|
||||
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
|
||||
$accessory->order_number = request('order_number');
|
||||
$accessory->manufacturer_id = request('manufacturer_id');
|
||||
$accessory->model_number = request('model_number');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->user_id = Auth::user()->id;
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = "accessory-".str_random(18).'.'.$ext;
|
||||
$path = public_path('/uploads/accessories');
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(null, 800, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
$accessory->image = $file_name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Was the accessory created?
|
||||
if ($accessory->save()) {
|
||||
$accessory->logCreate();
|
||||
// Redirect to the new accessory page
|
||||
return redirect()->to("admin/accessories")->with('success', trans('admin/accessories/message.create.success'));
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.create.success'));
|
||||
}
|
||||
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
|
||||
}
|
||||
|
||||
@@ -117,21 +122,17 @@ class AccessoriesController extends Controller
|
||||
* @param int $accessoryId
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit(Request $request, $accessoryId = null)
|
||||
public function edit(Request $request, $accessoryId = null)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($item = Accessory::find($accessoryId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($item)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
|
||||
if ($item = Accessory::find($accessoryId)) {
|
||||
$this->authorize($item);
|
||||
$category_type = 'accessory';
|
||||
return view('accessories/edit', compact('item'))->with('category_type', $category_type);
|
||||
}
|
||||
|
||||
return View::make('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());
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -142,54 +143,57 @@ class AccessoriesController extends Controller
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit(Request $request, $accessoryId = null)
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
// Redirect to the accessory index page
|
||||
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->name = e(Input::get('name'));
|
||||
$this->authorize($accessory);
|
||||
|
||||
if (e(Input::get('location_id')) == '') {
|
||||
$accessory->location_id = null;
|
||||
} else {
|
||||
$accessory->location_id = e(Input::get('location_id'));
|
||||
}
|
||||
$accessory->min_amt = e(Input::get('min_amt'));
|
||||
$accessory->category_id = e(Input::get('category_id'));
|
||||
$accessory->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$accessory->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$accessory->order_number = e(Input::get('order_number'));
|
||||
$accessory->model_number = e(Input::get('model_number'));
|
||||
// Update the accessory data
|
||||
$accessory->name = request('name');
|
||||
$accessory->location_id = request('location_id');
|
||||
$accessory->min_amt = request('min_amt');
|
||||
$accessory->category_id = request('category_id');
|
||||
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
|
||||
$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->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$accessory->purchase_date = null;
|
||||
} else {
|
||||
$accessory->purchase_date = e(Input::get('purchase_date'));
|
||||
if ($request->hasFile('image')) {
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = "accessory-".str_random(18).'.'.$ext;
|
||||
$path = public_path('/uploads/accessories');
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(null, 800, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) {
|
||||
unlink($path.'/'.$accessory->image);
|
||||
}
|
||||
|
||||
$accessory->image = $file_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$accessory->purchase_cost = null;
|
||||
} else {
|
||||
$accessory->purchase_cost = e(Input::get('purchase_cost'));
|
||||
}
|
||||
|
||||
$accessory->qty = e(Input::get('qty'));
|
||||
|
||||
// Was the accessory updated?
|
||||
// Was the accessory updated?
|
||||
if ($accessory->save()) {
|
||||
// Redirect to the updated accessory page
|
||||
return redirect()->to("admin/accessories")->with('success', trans('admin/accessories/message.update.success'));
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,26 +203,20 @@ class AccessoriesController extends Controller
|
||||
* @param int $accessoryId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete(Request $request, $accessoryId)
|
||||
public function destroy(Request $request, $accessoryId)
|
||||
{
|
||||
// Check if the blog post exists
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
|
||||
if ($accessory->hasUsers() > 0) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers())));
|
||||
} else {
|
||||
$accessory->delete();
|
||||
|
||||
// Redirect to the locations management page
|
||||
return redirect()->to('admin/accessories')->with('success', trans('admin/accessories/message.delete.success'));
|
||||
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers())));
|
||||
}
|
||||
$accessory->delete();
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -228,31 +226,19 @@ class AccessoriesController extends Controller
|
||||
* the content for the accessory detail view, which is generated in getDataView.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @param int $accessoryID
|
||||
* @see AccessoriesController::getDataView() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getView(Request $request, $accessoryID = null)
|
||||
public function show(Request $request, $accessoryID = null)
|
||||
{
|
||||
$accessory = Accessory::find($accessoryID);
|
||||
|
||||
$this->authorize('view', $accessory);
|
||||
if (isset($accessory->id)) {
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
} else {
|
||||
return View::make('accessories/view', compact('accessory'));
|
||||
}
|
||||
} else {
|
||||
// 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 view('accessories/view', compact('accessory'));
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', compact('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,15 +253,20 @@ class AccessoriesController extends Controller
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->to('accessories')->with('error', trans('admin/accessories/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
$users_list = Helper::usersList();
|
||||
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.');
|
||||
|
||||
|
||||
return View::make('accessories/checkout', compact('accessory'))->with('users_list', $users_list);
|
||||
|
||||
}
|
||||
|
||||
@@ -294,64 +285,28 @@ class AccessoriesController extends Controller
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->to('accessories')->with('error', trans('admin/accessories/message.user_not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.user_not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
if (!$user = User::find(Input::get('assigned_to'))) {
|
||||
return redirect()->to('admin/accessories')->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
|
||||
$accessory->assigned_to = e(Input::get('assigned_to'));
|
||||
$accessory->assigned_to = e(Input::get('assigned_to'));
|
||||
|
||||
$accessory->users()->attach($accessory->id, array(
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::user()->id,
|
||||
'assigned_to' => e(Input::get('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')
|
||||
]);
|
||||
|
||||
$logaction = $accessory->logCheckout(e(Input::get('note')));
|
||||
$logaction = $accessory->logCheckout(e(Input::get('note')), $user);
|
||||
|
||||
|
||||
|
||||
$admin_user = Auth::user();
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'Checked Out:',
|
||||
'value' => 'Accessory <'.config('app.url').'/admin/accessories/'.$accessory->id.'/view'.'|'.$accessory->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
|
||||
],
|
||||
[
|
||||
'title' => 'Note:',
|
||||
'value' => e(Input::get('note'))
|
||||
],
|
||||
]
|
||||
])->send('Accessory Checked Out');
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
$accessory_user = DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
|
||||
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['eula'] = $accessory->getEula();
|
||||
@@ -363,319 +318,83 @@ class AccessoriesController extends Controller
|
||||
$data['note'] = $logaction->note;
|
||||
$data['require_acceptance'] = $accessory->requireAcceptance();
|
||||
|
||||
|
||||
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()->to("admin/accessories")->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
|
||||
|
||||
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the accessory back into inventory
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @return View
|
||||
**/
|
||||
/**
|
||||
* Check the accessory back into inventory
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param integer $accessoryUserId
|
||||
* @param string $backto
|
||||
* @return View
|
||||
* @internal param int $accessoryId
|
||||
*/
|
||||
public function getCheckin(Request $request, $accessoryUserId = null, $backto = null)
|
||||
{
|
||||
// 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()->to('admin/accessories')->with('error', trans('admin/accessories/message.not_found'));
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
} else {
|
||||
return View::make('accessories/checkin', compact('accessory'))->with('backto', $backto);
|
||||
}
|
||||
$this->authorize('checkin', $accessory);
|
||||
return view('accessories/checkin', compact('accessory'))->with('backto', $backto);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 int $accessoryId
|
||||
* @return Redirect
|
||||
**/
|
||||
/**
|
||||
* 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 postCheckin(Request $request, $accessoryUserId = null, $backto = null)
|
||||
{
|
||||
// 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()->to('admin/accessories')->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);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($accessory)) {
|
||||
return redirect()->to('admin/accessories')->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$return_to = e($accessory_user->assigned_to);
|
||||
$logaction = $accessory->logCheckin(User::find($return_to), e(Input::get('note')));
|
||||
$admin_user = Auth::user();
|
||||
|
||||
|
||||
// Was the accessory updated?
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
|
||||
$slack_settings = [
|
||||
'username' => e($settings->botname),
|
||||
'channel' => e($settings->slack_channel),
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'Checked In:',
|
||||
'value' => class_basename(strtoupper($logaction->item_type)).' <'.config('app.url').'/admin/accessories/'.e($accessory->id).'/view'.'|'.e($accessory->name).'> checked in by <'.config('app.url').'/admin/users/'.e($admin_user->id).'/view'.'|'.e($admin_user->fullName()).'>.'
|
||||
],
|
||||
[
|
||||
'title' => 'Note:',
|
||||
'value' => e($logaction->note)
|
||||
],
|
||||
|
||||
]
|
||||
])->send('Accessory Checked In');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!is_null($accessory_user->assigned_to)) {
|
||||
$user = User::find($accessory_user->assigned_to);
|
||||
}
|
||||
|
||||
$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()->to("admin/users/".$return_to.'/view')->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
} else {
|
||||
return redirect()->to("admin/accessories/".$accessory->id."/view")->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
return redirect()->route("users.show", $return_to)->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
}
|
||||
return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success'));
|
||||
}
|
||||
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->to("admin/accessories")->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)",
|
||||
* "companyName": "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 int $accessoryId
|
||||
* @return string JSON containing accessories and their associated atrributes.
|
||||
**/
|
||||
public function getDatatable(Request $request)
|
||||
{
|
||||
$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')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
|
||||
$allowed_columns = ['name','min_amt','order_number','purchase_date','purchase_cost','companyName','category','model_number'];
|
||||
$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 'companyName':
|
||||
$accessories = $accessories->OrderCompany($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) {
|
||||
|
||||
$actions = '<nobr>';
|
||||
if (Gate::allows('accessories.checkout')) {
|
||||
$actions .= '<a href="' . route('checkout/accessory',
|
||||
$accessory->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($accessory->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
|
||||
}
|
||||
if (Gate::allows('accessories.edit')) {
|
||||
$actions .= '<a href="' . route('update/accessory',
|
||||
$accessory->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
}
|
||||
if (Gate::allows('accessories.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/accessory',
|
||||
$accessory->id) . '" data-content="' . trans('admin/accessories/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($accessory->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
$actions .= '</nobr>';
|
||||
$company = $accessory->company;
|
||||
|
||||
$rows[] = array(
|
||||
'name' => '<a href="'.url('admin/accessories/'.$accessory->id).'/view">'. $accessory->name.'</a>',
|
||||
'category' => ($accessory->category) ? (string)link_to('admin/settings/categories/'.$accessory->category->id.'/view', $accessory->category->name) : '',
|
||||
'model_number' => e($accessory->model_number),
|
||||
'qty' => e($accessory->qty),
|
||||
'order_number' => e($accessory->order_number),
|
||||
'min_amt' => e($accessory->min_amt),
|
||||
'location' => ($accessory->location) ? e($accessory->location->name): '',
|
||||
'purchase_date' => e($accessory->purchase_date),
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
|
||||
'numRemaining' => $accessory->numRemaining(),
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($company) ? '' : e($company->name),
|
||||
'manufacturer' => $accessory->manufacturer ? (string) link_to('/admin/settings/manufacturers/'.$accessory->manufacturer_id.'/view', $accessory->manufacturer->name) : ''
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total'=>$accessCount, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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('accessories.checkin')) {
|
||||
$actions .= '<a href="' . route('checkin/accessory',
|
||||
$user->pivot->id) . '" class="btn btn-info btn-sm">Checkin</a>';
|
||||
}
|
||||
|
||||
if (Gate::allows('users.view')) {
|
||||
$name = (string) link_to('/admin/users/'.$user->id.'/view', e($user->fullName()));
|
||||
} else {
|
||||
$name = e($user->fullName());
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'name' => $name,
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total'=>$count, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ActionlogController extends Controller
|
||||
{
|
||||
public function displaySig($filename)
|
||||
{
|
||||
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
$file = config('app.private_uploads') . '/signatures/' . $filename;
|
||||
$filetype = Helper::checkUploadIsImage($file);
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
291
app/Http/Controllers/Api/AccessoriesController.php
Normal file
291
app/Http/Controllers/Api/AccessoriesController.php
Normal file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
|
||||
|
||||
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$accessories->where('company_id','=',$request->input('company_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'));
|
||||
}
|
||||
|
||||
if ($request->filled('supplier_id')) {
|
||||
$accessories->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
$offset = (($accessories) && (request('offset') > $accessories->count())) ? 0 : request('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';
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$accessories = $accessories->OrderCategory($order);
|
||||
break;
|
||||
case 'company':
|
||||
$accessories = $accessories->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$accessories = $accessories->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$accessories->orderBy($sort, $order);
|
||||
|
||||
$total = $accessories->count();
|
||||
$accessories = $accessories->skip($offset)->take($limit)->get();
|
||||
return (new AccessoriesTransformer)->transformAccessories($accessories, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Accessory::class);
|
||||
$accessory = new Accessory;
|
||||
$accessory->fill($request->all());
|
||||
|
||||
if ($accessory->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function accessory_detail($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function checkedout($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('edit', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
$accessory->fill($request->all());
|
||||
|
||||
if ($accessory->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $accessory->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
$this->authorize($accessory);
|
||||
|
||||
if ($accessory->hasUsers() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', array('count'=> $accessory->hasUsers()))));
|
||||
}
|
||||
|
||||
$accessory->delete();
|
||||
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')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
250
app/Http/Controllers/Api/AssetMaintenancesController.php
Normal file
250
app/Http/Controllers/Api/AssetMaintenancesController.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
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
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @version v2.0
|
||||
*/
|
||||
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 index(Request $request)
|
||||
{
|
||||
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company', 'admin');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$maintenances = $maintenances->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if ($request->filled('asset_id')) {
|
||||
$maintenances->where('asset_id', '=', $request->input('asset_id'));
|
||||
}
|
||||
|
||||
$offset = (($maintenances) && (request('offset') > $maintenances->count())) ? 0 : request('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';
|
||||
|
||||
switch ($sort) {
|
||||
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, $total);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the new asset maintenance
|
||||
*
|
||||
* @see AssetMaintenancesController::getCreate() method for the form
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = e($request->input('cost'));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot add a maintenance for that asset'));
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->user_id = Auth::id();
|
||||
|
||||
if (( $assetMaintenance->completion_date !== null )
|
||||
&& ( $assetMaintenance->start_date !== "" )
|
||||
&& ( $assetMaintenance->start_date !== "0000-00-00" )
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores an update to an asset maintenance
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @param int $assetMaintenanceId
|
||||
* @param int $request
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
{
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
}
|
||||
|
||||
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
|
||||
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
|
||||
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost')));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
|
||||
if (( $assetMaintenance->completion_date == null )
|
||||
) {
|
||||
if (( $assetMaintenance->asset_maintenance_time !== 0 )
|
||||
|| ( !is_null($assetMaintenance->asset_maintenance_time) )
|
||||
) {
|
||||
$assetMaintenance->asset_maintenance_time = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (( $assetMaintenance->completion_date !== null )
|
||||
&& ( $assetMaintenance->start_date !== "" )
|
||||
&& ( $assetMaintenance->start_date !== "0000-00-00" )
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
|
||||
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an asset maintenance
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @param int $assetMaintenanceId
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function destroy($assetMaintenanceId)
|
||||
{
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot delete a maintenance for that asset'));
|
||||
}
|
||||
|
||||
$assetMaintenance->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* View an asset maintenance
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @param int $assetMaintenanceId
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function show($assetMaintenanceId)
|
||||
{
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
if (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot view a maintenance for that asset'));
|
||||
}
|
||||
return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
|
||||
|
||||
}
|
||||
}
|
||||
246
app/Http/Controllers/Api/AssetModelsController.php
Normal file
246
app/Http/Controllers/Api/AssetModelsController.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Asset;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls all actions related to asset models for
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @version v4.0
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
class AssetModelsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','assets_count'];
|
||||
|
||||
$assetmodels = AssetModel::select([
|
||||
'models.id',
|
||||
'models.image',
|
||||
'models.name',
|
||||
'model_number',
|
||||
'eol',
|
||||
'models.notes',
|
||||
'models.created_at',
|
||||
'category_id',
|
||||
'manufacturer_id',
|
||||
'depreciation_id',
|
||||
'fieldset_id',
|
||||
'models.deleted_at',
|
||||
'models.updated_at',
|
||||
])
|
||||
->with('category','depreciation', 'manufacturer','fieldset')
|
||||
->withCount('assets as assets_count');
|
||||
|
||||
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$assetmodels->onlyTrashed();
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($assetmodels) && (request('offset') > $assetmodels->count())) ? 0 : request('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'manufacturer':
|
||||
$assetmodels->OrderManufacturer($order);
|
||||
break;
|
||||
default:
|
||||
$assetmodels->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$total = $assetmodels->count();
|
||||
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
|
||||
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$assetmodel = new AssetModel;
|
||||
$assetmodel->fill($request->all());
|
||||
|
||||
if ($assetmodel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$assetmodel = AssetModel::withCount('assets as assets_count')->findOrFail($id);
|
||||
return (new AssetModelsTransformer)->transformAssetModel($assetmodel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource's assets
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function assets($id)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$assets = Asset::where('model_id','=',$id)->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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/models/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
$assetmodel = AssetModel::findOrFail($id);
|
||||
$this->authorize('delete', $assetmodel);
|
||||
|
||||
if ($assetmodel->assets()->count() > 0) {
|
||||
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/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);
|
||||
}
|
||||
|
||||
}
|
||||
799
app/Http/Controllers/Api/AssetsController.php
Normal file
799
app/Http/Controllers/Api/AssetsController.php
Normal file
@@ -0,0 +1,799 @@
|
||||
<?php
|
||||
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;
|
||||
use App\Models\Company;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Artisan;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use DB;
|
||||
use Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Paginator;
|
||||
use Response;
|
||||
use Slack;
|
||||
use Str;
|
||||
use TCPDF;
|
||||
use Validator;
|
||||
use View;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls all actions related to assets for
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
class AssetsController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns JSON listing of all assets
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request, $audit = null)
|
||||
{
|
||||
|
||||
$this->authorize('index', Asset::class);
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'asset_tag',
|
||||
'serial',
|
||||
'model_number',
|
||||
'last_checkout',
|
||||
'notes',
|
||||
'expected_checkin',
|
||||
'order_number',
|
||||
'image',
|
||||
'assigned_to',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'last_audit_date',
|
||||
'next_audit_date',
|
||||
'warranty_months',
|
||||
'checkout_counter',
|
||||
'checkin_counter',
|
||||
'requests_counter',
|
||||
];
|
||||
|
||||
$filter = array();
|
||||
|
||||
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.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
|
||||
// These are used by the API to query against specific ID numbers.
|
||||
// They are also used by the individual searches on detail pages like
|
||||
// locations, etc.
|
||||
if ($request->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('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'))) : '';
|
||||
|
||||
$offset = (($assets) && (request('offset') > $assets->count())) ? 0 : request('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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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->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->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->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->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':
|
||||
// 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 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;
|
||||
case 'model_number':
|
||||
$assets->OrderModelNumber($order);
|
||||
break;
|
||||
case 'category':
|
||||
$assets->OrderCategory($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$assets->OrderManufacturer($order);
|
||||
break;
|
||||
case 'company':
|
||||
$assets->OrderCompany($order);
|
||||
break;
|
||||
case 'location':
|
||||
$assets->OrderLocation($order);
|
||||
case 'rtd_location':
|
||||
$assets->OrderRtdLocation($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$assets->OrderStatus($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$assets->OrderSupplier($order);
|
||||
break;
|
||||
case 'assigned_to':
|
||||
$assets->OrderAssigned($order);
|
||||
break;
|
||||
default:
|
||||
$assets->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$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>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function show($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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a POST request to create a new asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(AssetRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
$asset = new Asset();
|
||||
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
|
||||
|
||||
$asset->name = $request->get('name');
|
||||
$asset->serial = $request->get('serial');
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id'));
|
||||
$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::autoincrement_asset());
|
||||
$asset->user_id = Auth::id();
|
||||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = $request->get('status_id', 0);
|
||||
$asset->warranty_months = $request->get('warranty_months', null);
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
|
||||
$asset->purchase_date = $request->get('purchase_date', null);
|
||||
$asset->assigned_to = $request->get('assigned_to', null);
|
||||
$asset->supplier_id = $request->get('supplier_id', 0);
|
||||
$asset->requestable = $request->get('requestable', 0);
|
||||
$asset->rtd_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) && ($model->fieldset)) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
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 (isset($target)) {
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a POST request to update an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
|
||||
$asset->fill($request->all());
|
||||
|
||||
($request->filled('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
|
||||
($request->filled('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : null;
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
// Update custom fields
|
||||
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($request->has($field->convertUnicodeDbSlug())) {
|
||||
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->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;
|
||||
} 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')), $location);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a given asset (mark as deleted).
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
|
||||
$this->authorize('delete', $asset);
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update(array('assigned_to' => null));
|
||||
|
||||
$asset->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.delete.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checkout an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
|
||||
if (!$asset->availableForCheckout()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
$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
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
}
|
||||
$error_payload['target_id'] = $request->input('assigned_asset');
|
||||
$error_payload['target_type'] = 'asset';
|
||||
|
||||
} elseif (request('checkout_to_type')=='user') {
|
||||
// Fetch the target and set the asset's new location_id
|
||||
$target = User::find(request('assigned_user'));
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_user');
|
||||
$error_payload['target_type'] = 'user';
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!isset($target)) {
|
||||
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);
|
||||
|
||||
// Set the location ID to the RTD location id if there is one
|
||||
if ($asset->rtd_location_id!='') {
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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')));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checkin an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function checkin(Request $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
|
||||
$user = $asset->assignedUser;
|
||||
if (is_null($target = $asset->assignedTo)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], 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->accepted = null;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
$asset->logCheckin($target, e(request('note')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark an asset as audited
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function audit(Request $request) {
|
||||
|
||||
|
||||
$this->authorize('audit', Asset::class);
|
||||
$rules = array(
|
||||
'asset_tag' => 'required',
|
||||
'location_id' => 'exists:locations,id|nullable|numeric',
|
||||
'next_audit_date' => 'date|nullable'
|
||||
);
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
171
app/Http/Controllers/Api/CategoriesController.php
Normal file
171
app/Http/Controllers/Api/CategoriesController.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
$allowed_columns = ['id', 'name','category_type', 'category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count','licenses_count', 'image'];
|
||||
|
||||
$categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email','image'])
|
||||
->withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count','licenses as licenses_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$categories = $categories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($categories) && (request('offset') > $categories->count())) ? 0 : request('offset', 0);
|
||||
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
|
||||
$categories->orderBy($sort, $order);
|
||||
|
||||
$total = $categories->count();
|
||||
$categories = $categories->skip($offset)->take($limit)->get();
|
||||
return (new CategoriesTransformer)->transformCategories($categories, $total);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Category::class);
|
||||
$category = new Category;
|
||||
$category->fill($request->all());
|
||||
|
||||
if ($category->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $category->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
return (new CategoriesTransformer)->transformCategory($category);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
$category->fill($request->all());
|
||||
|
||||
if ($category->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $category->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
|
||||
if ($category->has_models() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'model'])));
|
||||
} elseif ($category->accessories()->count() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'accessory'])));
|
||||
} elseif ($category->consumables()->count() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'consumable'])));
|
||||
} elseif ($category->components()->count() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>'component'])));
|
||||
}
|
||||
$category->delete();
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
189
app/Http/Controllers/Api/CompaniesController.php
Normal file
189
app/Http/Controllers/Api/CompaniesController.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Transformers\CompaniesTransformer;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
'assets_count',
|
||||
'licenses_count',
|
||||
'accessories_count',
|
||||
'consumables_count',
|
||||
'components_count',
|
||||
];
|
||||
|
||||
$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->filled('search')) {
|
||||
$companies->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($companies) && (request('offset') > $companies->count())) ? 0 : request('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);
|
||||
|
||||
$total = $companies->count();
|
||||
$companies = $companies->skip($offset)->take($limit)->get();
|
||||
return (new CompaniesTransformer)->transformCompanies($companies, $total);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
$company = new Company;
|
||||
$company->fill($request->all());
|
||||
|
||||
if ($company->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new CompaniesTransformer)->transformCompany($company), trans('admin/companies/message.create.success')));
|
||||
}
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('error', null, $company->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
return (new CompaniesTransformer)->transformCompany($company);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$company->fill($request->all());
|
||||
|
||||
if ($company->save()) {
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('success', (new CompaniesTransformer)->transformCompany($company), trans('admin/companies/message.update.success')));
|
||||
}
|
||||
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('error', null, $company->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$this->authorize('delete', $company);
|
||||
|
||||
try {
|
||||
$company->delete();
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('success', null, trans('admin/companies/message.delete.success')));
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
/*
|
||||
* NOTE: This happens when there's a foreign key constraint violation
|
||||
* For example when rows in other tables are referencing this company
|
||||
*/
|
||||
if ($exception->getCode() == 23000) {
|
||||
return response()
|
||||
->json(Helper::formatStandardApiResponse('error', null, trans('admin/companies/message.assoc_users')));
|
||||
|
||||
} else {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
175
app/Http/Controllers/Api/ComponentsController.php
Normal file
175
app/Http/Controllers/Api/ComponentsController.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\ComponentsTransformer;
|
||||
use App\Http\Transformers\ComponentsAssetsTransformer;
|
||||
use App\Models\Component;
|
||||
use App\Models\Company;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
class ComponentsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')
|
||||
->with('company', 'location', 'category'));
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$components = $components->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$components->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
$offset = (($components) && (request('offset') > $components->count())) ? 0 : request('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';
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$components = $components->OrderCategory($order);
|
||||
break;
|
||||
case 'location':
|
||||
$components = $components->OrderLocation($order);
|
||||
break;
|
||||
case 'company':
|
||||
$components = $components->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$components = $components->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$total = $components->count();
|
||||
$components = $components->skip($offset)->take($limit)->get();
|
||||
return (new ComponentsTransformer)->transformComponents($components, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Component::class);
|
||||
$component = new Component;
|
||||
$component->fill($request->all());
|
||||
|
||||
if ($component->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $component->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
|
||||
if ($component) {
|
||||
return (new ComponentsTransformer)->transformComponent($component);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
$component->fill($request->all());
|
||||
|
||||
if ($component->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $component->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
$this->authorize('delete', $component);
|
||||
$component->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.delete.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all assets attached to a component
|
||||
*
|
||||
* @author [A. Bergamasco] [@vjandrea]
|
||||
* @since [v4.0]
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getAssets(Request $request, $id)
|
||||
{
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
|
||||
$component = Component::findOrFail($id);
|
||||
$assets = $component->assets();
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
|
||||
}
|
||||
}
|
||||
197
app/Http/Controllers/Api/ConsumablesController.php
Normal file
197
app/Http/Controllers/Api/ConsumablesController.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
class ConsumablesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('index', Consumable::class);
|
||||
$consumables = Company::scopeCompanyables(
|
||||
Consumable::select('consumables.*')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
||||
);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$consumables->where('company_id','=',$request->input('company_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 = (($consumables) && (request('offset') > $consumables->count())) ? 0 : request('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';
|
||||
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$consumables = $consumables->OrderCategory($order);
|
||||
break;
|
||||
case 'location':
|
||||
$consumables = $consumables->OrderLocation($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$consumables = $consumables->OrderManufacturer($order);
|
||||
break;
|
||||
case 'company':
|
||||
$consumables = $consumables->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$consumables = $consumables->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$total = $consumables->count();
|
||||
$consumables = $consumables->skip($offset)->take($limit)->get();
|
||||
return (new ConsumablesTransformer)->transformConsumables($consumables, $total);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable;
|
||||
$consumable->fill($request->all());
|
||||
|
||||
if ($consumable->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $consumable->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
return (new ConsumablesTransformer)->transformConsumable($consumable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
$consumable->fill($request->all());
|
||||
|
||||
if ($consumable->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $consumable->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
$this->authorize('delete', $consumable);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
192
app/Http/Controllers/Api/CustomFieldsController.php
Normal file
192
app/Http/Controllers/Api/CustomFieldsController.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Reorder the custom fields within a fieldset
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @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();
|
||||
|
||||
$items = $request->input('item');
|
||||
|
||||
foreach ($items as $order => $field_id) {
|
||||
$order_array[$field_id] = $order;
|
||||
}
|
||||
|
||||
foreach ($fieldset->fields as $field) {
|
||||
$fields[$field->id] = ['required' => $field->pivot->required, 'order' => $order_array[$field->id]];
|
||||
}
|
||||
|
||||
return $fieldset->fields()->sync($fields);
|
||||
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function destroy($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.'));
|
||||
}
|
||||
|
||||
$field->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.field.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
179
app/Http/Controllers/Api/CustomFieldsetsController.php
Normal file
179
app/Http/Controllers/Api/CustomFieldsetsController.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use View;
|
||||
use App\Models\CustomFieldset;
|
||||
use App\Models\CustomField;
|
||||
use Input;
|
||||
use Validator;
|
||||
use Redirect;
|
||||
use App\Models\AssetModel;
|
||||
use Lang;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Transformers\CustomFieldsTransformer;
|
||||
use App\Http\Transformers\CustomFieldsetsTransformer;
|
||||
use App\Http\Requests\AssetRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Custom Asset Fieldsets for
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @todo Improve documentation here.
|
||||
* @todo Check for raw DB queries and try to convert them to query builder statements
|
||||
* @version v2.0
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [Josh Gibson]
|
||||
*/
|
||||
|
||||
class CustomFieldsetsController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Shows the given fieldset and its fields
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @author [Josh Gibson]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomFieldset::class);
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the given fieldset and its fields
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @author [Josh Gibson]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
if ($fieldset = CustomFieldset::find($id)) {
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldset($fieldset);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/custom_fields/message.fieldset.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
if ($fieldset->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $fieldset->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
$fieldset = new CustomFieldset;
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
if ($fieldset->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $fieldset, trans('admin/custom_fields/message.fieldset.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $fieldset->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a custom fieldset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', CustomFieldset::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
|
||||
$modelsCount = $fieldset->models->count();
|
||||
$fieldsCount = $fieldset->fields->count();
|
||||
|
||||
if (($modelsCount > 0) || ($fieldsCount > 0) ){
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Fieldset is in use.'));
|
||||
}
|
||||
|
||||
if ($fieldset->delete()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/custom_fields/message.fieldset.delete.success')));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
165
app/Http/Controllers/Api/DepartmentsController.php
Normal file
165
app/Http/Controllers/Api/DepartmentsController.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
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>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$allowed_columns = ['id','name','image','users_count'];
|
||||
|
||||
$departments = Department::select([
|
||||
'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->filled('search')) {
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($departments) && (request('offset') > $departments->count())) ? 0 : request('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';
|
||||
|
||||
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();
|
||||
return (new DepartmentsTransformer)->transformDepartments($departments, $total);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department->user_id = Auth::user()->id;
|
||||
$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')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$department = Department::findOrFail($id);
|
||||
return (new DepartmentsTransformer)->transformDepartment($department);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Validates and deletes selected location.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$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')));
|
||||
}
|
||||
|
||||
$department->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/departments/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)
|
||||
{
|
||||
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
130
app/Http/Controllers/Api/DepreciationsController.php
Normal file
130
app/Http/Controllers/Api/DepreciationsController.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Depreciation;
|
||||
use App\Http\Transformers\DepreciationsTransformer;
|
||||
|
||||
class DepreciationsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = ['id','name','created_at'];
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($depreciations) && (request('offset') > $depreciations->count())) ? 0 : request('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);
|
||||
|
||||
$total = $depreciations->count();
|
||||
$depreciations = $depreciations->skip($offset)->take($limit)->get();
|
||||
return (new DepreciationsTransformer)->transformDepreciations($depreciations, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
$depreciation = new Depreciation;
|
||||
$depreciation->fill($request->all());
|
||||
|
||||
if ($depreciation->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $depreciation, trans('admin/depreciations/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $depreciation->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$depreciation = Depreciation::findOrFail($id);
|
||||
return (new DepreciationsTransformer)->transformDepreciation($depreciation);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Depreciation::class);
|
||||
$depreciation = Depreciation::findOrFail($id);
|
||||
$depreciation->fill($request->all());
|
||||
|
||||
if ($depreciation->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $depreciation, trans('admin/depreciations/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $depreciation->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Depreciation::class);
|
||||
$depreciation = Depreciation::findOrFail($id);
|
||||
$this->authorize('delete', $depreciation);
|
||||
|
||||
if ($depreciation->has_models() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('admin/depreciations/message.assoc_users')));
|
||||
}
|
||||
|
||||
$depreciation->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/depreciations/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
124
app/Http/Controllers/Api/GroupsController.php
Normal file
124
app/Http/Controllers/Api/GroupsController.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Group;
|
||||
use App\Http\Transformers\GroupsTransformer;
|
||||
|
||||
class GroupsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Group::class);
|
||||
$allowed_columns = ['id','name','created_at', 'users_count'];
|
||||
|
||||
$groups = Group::select('id','name','permissions','created_at','updated_at')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($groups) && (request('offset') > $groups->count())) ? 0 : request('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);
|
||||
|
||||
$total = $groups->count();
|
||||
$groups = $groups->skip($offset)->take($limit)->get();
|
||||
return (new GroupsTransformer)->transformGroups($groups, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Group::class);
|
||||
$group = new Group;
|
||||
$group->fill($request->all());
|
||||
|
||||
if ($group->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
return (new GroupsTransformer)->transformGroup($group);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
$group->fill($request->all());
|
||||
|
||||
if ($group->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $group->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
$this->authorize('delete', $group);
|
||||
$group->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/groups/message.delete.success')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
173
app/Http/Controllers/Api/ImportController.php
Normal file
173
app/Http/Controllers/Api/ImportController.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ItemImportRequest;
|
||||
use App\Http\Transformers\ImportsTransformer;
|
||||
use App\Models\Company;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Http\Request;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
return (new ImportsTransformer)->transformImports($imports);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and store a CSV upload file.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
$this->authorize('import');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$files = Input::file('files');
|
||||
$path = config('app.private_uploads').'/imports';
|
||||
$results = [];
|
||||
$import = new Import;
|
||||
foreach ($files as $file) {
|
||||
if (!in_array($file->getMimeType(), array(
|
||||
'application/vnd.ms-excel',
|
||||
'text/csv',
|
||||
'text/plain',
|
||||
'text/comma-separated-values',
|
||||
'text/tsv'))) {
|
||||
$results['error']='File type must be CSV';
|
||||
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 {
|
||||
$file->move($path, $date.'-'.$fixed_filename);
|
||||
} catch (FileException $exception) {
|
||||
$results['error']=trans('admin/hardware/message.upload.error');
|
||||
if (config('app.debug')) {
|
||||
$results['error'].= ' ' . $exception->getMessage();
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
|
||||
}
|
||||
$file_name = date('Y-m-d-his').'-'.$fixed_filename;
|
||||
$import->file_path = $file_name;
|
||||
$import->filesize = filesize($path.'/'.$file_name);
|
||||
$import->save();
|
||||
$results[] = $import;
|
||||
}
|
||||
$results = (new ImportsTransformer)->transformImports($results);
|
||||
return [
|
||||
'files' => $results,
|
||||
];
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.feature_disabled')), 500);
|
||||
}
|
||||
/**
|
||||
* Processes the specified Import.
|
||||
*
|
||||
* @param \App\Import $import
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function process(ItemImportRequest $request, $import_id)
|
||||
{
|
||||
$this->authorize('import');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
$errors = $request->import(Import::find($import_id));
|
||||
$redirectTo = "hardware.index";
|
||||
switch ($request->get('import-type')) {
|
||||
case "asset":
|
||||
$redirectTo = "hardware.index";
|
||||
break;
|
||||
case "accessory":
|
||||
$redirectTo = "accessories.index";
|
||||
break;
|
||||
case "consumable":
|
||||
$redirectTo = "consumables.index";
|
||||
break;
|
||||
case "component":
|
||||
$redirectTo = "components.index";
|
||||
break;
|
||||
case "license":
|
||||
$redirectTo = "licenses.index";
|
||||
break;
|
||||
case "user":
|
||||
$redirectTo = "users.index";
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
return response()->json(Helper::formatStandardApiResponse('import-errors', null, $errors), 500);
|
||||
}
|
||||
//Flash message before the redirect
|
||||
Session::flash('success', trans('admin/hardware/message.import.success'));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, ['redirect_url' => route($redirectTo)]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Import $import
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($import_id)
|
||||
{
|
||||
$this->authorize('import');
|
||||
$import = Import::find($import_id);
|
||||
try {
|
||||
unlink(config('app.private_uploads').'/imports/'.$import->file_path);
|
||||
$import->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.import.file_delete_success')));
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.import.file_delete_error')), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
252
app/Http/Controllers/Api/LicensesController.php
Normal file
252
app/Http/Controllers/Api/LicensesController.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\LicenseSeatsTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'freeSeats', 'supplier','category')->withCount('freeSeats as free_seats_count'));
|
||||
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$licenses->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$licenses->where('licenses.name','=',$request->input('name'));
|
||||
}
|
||||
|
||||
if ($request->filled('product_key')) {
|
||||
$licenses->where('licenses.serial','=',$request->input('product_key'));
|
||||
}
|
||||
|
||||
if ($request->filled('order_number')) {
|
||||
$licenses->where('order_number','=',$request->input('order_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('purchase_order')) {
|
||||
$licenses->where('purchase_order','=',$request->input('purchase_order'));
|
||||
}
|
||||
|
||||
if ($request->filled('license_name')) {
|
||||
$licenses->where('license_name','=',$request->input('license_name'));
|
||||
}
|
||||
|
||||
if ($request->filled('license_email')) {
|
||||
$licenses->where('license_email','=',$request->input('license_email'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$licenses->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('supplier_id')) {
|
||||
$licenses->where('supplier_id','=',$request->input('supplier_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->filled('supplier_id')) {
|
||||
$licenses->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
$offset = (($licenses) && (request('offset') > $licenses->count())) ? 0 : request('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->leftJoin('manufacturers', 'licenses.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$licenses = $licenses->leftJoin('suppliers', 'licenses.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
|
||||
break;
|
||||
case 'category':
|
||||
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
|
||||
break;
|
||||
case 'company':
|
||||
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
//
|
||||
$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()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
$license = License::findOrFail($id);
|
||||
$license = $license->load('assignedusers', 'licenseSeats.user', 'licenseSeats.asset');
|
||||
return (new LicensesTransformer)->transformLicense($license);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
//
|
||||
$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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
//
|
||||
$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_id', $licenseId)->with('license', 'user', 'asset');
|
||||
|
||||
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
|
||||
|
||||
$limit = request('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$total = $seats->count();
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
260
app/Http/Controllers/Api/LocationsController.php
Normal file
260
app/Http/Controllers/Api/LocationsController.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$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::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');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$locations = $locations->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
$offset = (($locations) && (request('offset') > $locations->count())) ? 0 : request('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';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$location = new Location;
|
||||
$location->fill($request->all());
|
||||
|
||||
if ($location->save()) {
|
||||
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()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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('error', null, $location->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
$this->authorize('delete', $location);
|
||||
$location->delete();
|
||||
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')) {
|
||||
\Log::debug('Searching... ');
|
||||
$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;
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
168
app/Http/Controllers/Api/ManufacturersController.php
Normal file
168
app/Http/Controllers/Api/ManufacturersController.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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','image', 'assets_count', 'consumables_count', 'components_count', 'licenses_count'];
|
||||
|
||||
$manufacturers = Manufacturer::select(
|
||||
array('id','name','url','support_url','support_email','support_phone','created_at','updated_at','image', '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->filled('search')) {
|
||||
$manufacturers = $manufacturers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$offset = (($manufacturers) && (request('offset') > $manufacturers->count())) ? 0 : request('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);
|
||||
|
||||
$total = $manufacturers->count();
|
||||
$manufacturers = $manufacturers->skip($offset)->take($limit)->get();
|
||||
return (new ManufacturersTransformer)->transformManufacturers($manufacturers, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->fill($request->all());
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $manufacturer->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer->fill($request->all());
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $manufacturer->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$this->authorize('delete', $manufacturer);
|
||||
$manufacturer->delete();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
66
app/Http/Controllers/Api/ReportsController.php
Normal file
66
app/Http/Controllers/Api/ReportsController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
|
||||
class ReportsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Returns Activity Report JSON.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target','location');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
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->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->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',
|
||||
'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';
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
$total = $actionlogs->count();
|
||||
$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);
|
||||
|
||||
}
|
||||
}
|
||||
148
app/Http/Controllers/Api/SettingsController.php
Normal file
148
app/Http/Controllers/Api/SettingsController.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
|
||||
public function ldaptest()
|
||||
{
|
||||
|
||||
if (Setting::getSettings()->ldap_enabled!='1') {
|
||||
\Log::debug('LDAP is not enabled cannot test.');
|
||||
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
|
||||
}
|
||||
|
||||
\Log::debug('Preparing to test LDAP connection');
|
||||
|
||||
try {
|
||||
$connection = Ldap::connectToLdap();
|
||||
try {
|
||||
\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('Bind failed');
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
//return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed but we cannot debug it any further on our end.');
|
||||
return response()->json(['message' => $e->getMessage()], 600);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
//return response()->json(['message' => $e->getMessage()], 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Connection failed');
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
252
app/Http/Controllers/Api/StatuslabelsController.php
Normal file
252
app/Http/Controllers/Api/StatuslabelsController.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Asset;
|
||||
use App\Http\Transformers\StatuslabelsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
|
||||
class StatuslabelsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$allowed_columns = ['id','name','created_at', 'assets_count','color','default_label'];
|
||||
|
||||
$statuslabels = Statuslabel::withCount('assets as assets_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$statuslabels = $statuslabels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($statuslabels) && (request('offset') > $statuslabels->count())) ? 0 : request('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);
|
||||
|
||||
$total = $statuslabels->count();
|
||||
$statuslabels = $statuslabels->skip($offset)->take($limit)->get();
|
||||
return (new StatuslabelsTransformer)->transformStatuslabels($statuslabels, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
$request->except('deployable', 'pending','archived');
|
||||
|
||||
if (!$request->filled('type')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, ["type" => ["Status label type is required."]]),500);
|
||||
}
|
||||
|
||||
$statuslabel = new Statuslabel;
|
||||
$statuslabel->fill($request->all());
|
||||
|
||||
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
|
||||
if ($statuslabel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statuslabel->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
return (new StatuslabelsTransformer)->transformStatuslabel($statuslabel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
|
||||
$request->except('deployable', 'pending','archived');
|
||||
|
||||
if (!$request->filled('type')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Status label type is required.'));
|
||||
}
|
||||
|
||||
$statuslabel->fill($request->all());
|
||||
|
||||
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
|
||||
if ($statuslabel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statuslabel->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
$this->authorize('delete', $statuslabel);
|
||||
|
||||
// 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')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Show a count of assets by status label for pie chart
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
|
||||
public function getAssetCountByStatuslabel()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets as assets_count')->get();
|
||||
|
||||
$labels=[];
|
||||
$points=[];
|
||||
$colors=[];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$colors_array = array_merge($colors, Helper::chartColors());
|
||||
|
||||
$result= [
|
||||
"labels" => $labels,
|
||||
"datasets" => [ [
|
||||
"data" => $points,
|
||||
"backgroundColor" => $colors_array,
|
||||
"hoverBackgroundColor" => $colors_array
|
||||
]]
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function assets(Request $request, $id)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('status_id','=',$id);
|
||||
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name'
|
||||
];
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$assets->orderBy($sort, $order);
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
|
||||
return (new AssetsTransformer)->transformAssets($assets, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a boolean response based on whether the status label
|
||||
* is one that is deployable.
|
||||
*
|
||||
* This is used by the hardware create/edit view to determine whether
|
||||
* we should provide a dropdown of users for them to check the asset out to.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return Bool
|
||||
*/
|
||||
public function checkIfDeployable($id) {
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
if ($statuslabel->getStatuslabelType()=='deployable') {
|
||||
return '1';
|
||||
}
|
||||
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
177
app/Http/Controllers/Api/SuppliersController.php
Normal file
177
app/Http/Controllers/Api/SuppliersController.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count'];
|
||||
|
||||
$suppliers = Supplier::select(
|
||||
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
|
||||
)->withCount('assets as assets_count')->withCount('licenses as licenses_count')->withCount('accessories as accessories_count');
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers = $suppliers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = (($suppliers) && (request('offset') > $suppliers->count())) ? 0 : request('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);
|
||||
|
||||
$total = $suppliers->count();
|
||||
$suppliers = $suppliers->skip($offset)->take($limit)->get();
|
||||
return (new SuppliersTransformer)->transformSuppliers($suppliers, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('create', Supplier::class);
|
||||
$supplier = new Supplier;
|
||||
$supplier->fill($request->all());
|
||||
|
||||
if ($supplier->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $supplier->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$supplier = Supplier::findOrFail($id);
|
||||
return (new SuppliersTransformer)->transformSupplier($supplier);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('update', Supplier::class);
|
||||
$supplier = Supplier::findOrFail($id);
|
||||
$supplier->fill($request->all());
|
||||
|
||||
if ($supplier->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $supplier->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
398
app/Http/Controllers/Api/UsersController.php
Normal file
398
app/Http/Controllers/Api/UsersController.php
Normal file
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\UsersTransformer;
|
||||
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;
|
||||
|
||||
class UsersController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$users = User::select([
|
||||
'users.activated',
|
||||
'users.address',
|
||||
'users.avatar',
|
||||
'users.city',
|
||||
'users.company_id',
|
||||
'users.country',
|
||||
'users.created_at',
|
||||
'users.deleted_at',
|
||||
'users.department_id',
|
||||
'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->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('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'));
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$offset = (($users) && (request('offset') > $users->count())) ? 0 : request('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':
|
||||
$users = $users->OrderManager($order);
|
||||
break;
|
||||
case 'location':
|
||||
$users = $users->OrderLocation($order);
|
||||
break;
|
||||
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', '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.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(SaveUserRequest $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = new User;
|
||||
$user->fill($request->all());
|
||||
|
||||
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
$user->password = bcrypt($request->get('password', $tmp_pass));
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
return (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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(SaveUserRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
$user = User::findOrFail($id);
|
||||
$user->fill($request->all());
|
||||
|
||||
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'));
|
||||
}
|
||||
|
||||
// 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')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->authorize('delete', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 response()->json($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'));
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,9 @@ use Input;
|
||||
use Lang;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Redirect;
|
||||
use Response;
|
||||
use Slack;
|
||||
use Str;
|
||||
use App\Models\Supplier;
|
||||
use TCPDF;
|
||||
use Validator;
|
||||
use View;
|
||||
@@ -22,6 +20,7 @@ use App\Models\Asset;
|
||||
use App\Helpers\Helper;
|
||||
use Auth;
|
||||
use Gate;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Asset Maintenance for
|
||||
@@ -43,7 +42,7 @@ class AssetMaintenancesController extends Controller
|
||||
*/
|
||||
private static function getInsufficientPermissionsRedirect()
|
||||
{
|
||||
return redirect()->route('asset_maintenances')
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
|
||||
@@ -58,129 +57,39 @@ class AssetMaintenancesController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
public function index()
|
||||
{
|
||||
|
||||
return View::make('asset_maintenances/index');
|
||||
return view('asset_maintenances/index');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$maintenances = AssetMaintenance::with('asset', 'supplier', 'asset.company','admin');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$maintenances = $maintenances->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$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(Input::get('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('assets.edit')) {
|
||||
$actions .= '<nobr><a href="' . route('update/asset_maintenance',
|
||||
$maintenance->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/asset_maintenance',
|
||||
$maintenance->id) . '" data-content="' . trans('admin/asset_maintenances/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($maintenance->title) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></nobr>';
|
||||
}
|
||||
|
||||
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('/hardware/'.$maintenance->asset->id.'/view', $maintenance->asset->showAssetName()) : 'Deleted Asset' ,
|
||||
'title' => $maintenance->title,
|
||||
'notes' => $maintenance->notes,
|
||||
'supplier' => ($maintenance->supplier) ? (string)link_to('/admin/settings/suppliers/'.$maintenance->supplier->id.'/view', $maintenance->supplier->name) : '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('/admin/users/'.$maintenance->admin->id.'/view', $maintenance->admin->fullName()) : '',
|
||||
'actions' => $actions,
|
||||
'companyName' => ($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.
|
||||
*
|
||||
* @see AssetMaintenancesController::postCreate() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCreate($assetId = null)
|
||||
* Returns a form view to create a new asset maintenance.
|
||||
*
|
||||
* @see AssetMaintenancesController::postCreate() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
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
|
||||
$selectedAsset = $assetId;
|
||||
|
||||
$assets = Helper::detailedAssetList();
|
||||
|
||||
$supplier_list = Helper::suppliersList();
|
||||
|
||||
// Render the view
|
||||
return View::make('asset_maintenances/edit')
|
||||
->with('asset_list', $assets)
|
||||
->with('selectedAsset', $selectedAsset)
|
||||
->with('supplier_list', $supplier_list)
|
||||
return view('asset_maintenances/edit')
|
||||
->with('asset', $asset)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', new AssetMaintenance);
|
||||
}
|
||||
@@ -194,62 +103,29 @@ class AssetMaintenancesController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
public function postCreate()
|
||||
public function store(Request $request)
|
||||
{
|
||||
|
||||
// get the POST data
|
||||
$new = Input::all();
|
||||
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = e($request->input('cost'));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
|
||||
if (e(Input::get('supplier_id')) == '') {
|
||||
$assetMaintenance->supplier_id = null;
|
||||
} else {
|
||||
$assetMaintenance->supplier_id = e(Input::get('supplier_id'));
|
||||
}
|
||||
|
||||
if (e(Input::get('is_warranty')) == '') {
|
||||
$assetMaintenance->is_warranty = 0;
|
||||
} else {
|
||||
$assetMaintenance->is_warranty = e(Input::get('is_warranty'));
|
||||
}
|
||||
|
||||
if (e(Input::get('cost')) == '') {
|
||||
$assetMaintenance->cost = '';
|
||||
} else {
|
||||
$assetMaintenance->cost = Helper::ParseFloat(e(Input::get('cost')));
|
||||
}
|
||||
|
||||
if (e(Input::get('notes')) == '') {
|
||||
$assetMaintenance->notes = null;
|
||||
} else {
|
||||
$assetMaintenance->notes = e(Input::get('notes'));
|
||||
}
|
||||
|
||||
$asset = Asset::find(e(Input::get('asset_id')));
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = e(Input::get('asset_id'));
|
||||
$assetMaintenance->asset_maintenance_type = e(Input::get('asset_maintenance_type'));
|
||||
$assetMaintenance->title = e(Input::get('title'));
|
||||
$assetMaintenance->start_date = e(Input::get('start_date'));
|
||||
$assetMaintenance->completion_date = e(Input::get('completion_date'));
|
||||
$assetMaintenance->user_id = Auth::user()->id;
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->user_id = Auth::id();
|
||||
|
||||
if (( $assetMaintenance->completion_date == "" )
|
||||
|| ( $assetMaintenance->completion_date == "0000-00-00" )
|
||||
) {
|
||||
$assetMaintenance->completion_date = null;
|
||||
}
|
||||
|
||||
if (( $assetMaintenance->completion_date !== "" )
|
||||
&& ( $assetMaintenance->completion_date !== "0000-00-00" )
|
||||
if (( $assetMaintenance->completion_date !== null )
|
||||
&& ( $assetMaintenance->start_date !== "" )
|
||||
&& ( $assetMaintenance->start_date !== "0000-00-00" )
|
||||
) {
|
||||
@@ -260,17 +136,13 @@ class AssetMaintenancesController extends Controller
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->to("admin/asset_maintenances")
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,13 +155,16 @@ class AssetMaintenancesController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEdit($assetMaintenanceId = null)
|
||||
public function edit($assetMaintenanceId = null)
|
||||
{
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the improvement management page
|
||||
return redirect()->to('admin/asset_maintenances')
|
||||
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();
|
||||
}
|
||||
@@ -311,88 +186,57 @@ class AssetMaintenancesController extends Controller
|
||||
'' => 'Select an improvement type',
|
||||
] + AssetMaintenance::getImprovementOptions();
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::with('model','assignedUser')->get(), 'assets.company_id')->lists('detailed_name', 'id');
|
||||
// Get Supplier List
|
||||
$supplier_list = Helper::suppliersList();
|
||||
|
||||
// Render the view
|
||||
return View::make('asset_maintenances/edit')
|
||||
->with('asset_list', $assets)
|
||||
return view('asset_maintenances/edit')
|
||||
->with('selectedAsset', null)
|
||||
->with('supplier_list', $supplier_list)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', $assetMaintenance);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores an update to an asset maintenance
|
||||
*
|
||||
* @see AssetMaintenancesController::postEdit() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @param int $assetMaintenanceId
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
public function postEdit($assetMaintenanceId = null)
|
||||
* Validates and stores an update to an asset maintenance
|
||||
*
|
||||
* @see AssetMaintenancesController::postEdit() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @param Request $request
|
||||
* @param int $assetMaintenanceId
|
||||
* @return mixed
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
{
|
||||
|
||||
// get the POST data
|
||||
$new = Input::all();
|
||||
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->to('admin/asset_maintenances')
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
|
||||
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
|
||||
$assetMaintenance->cost = Helper::ParseFloat(e($request->input('cost')));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
|
||||
|
||||
if (e(Input::get('supplier_id')) == '') {
|
||||
$assetMaintenance->supplier_id = null;
|
||||
} else {
|
||||
$assetMaintenance->supplier_id = e(Input::get('supplier_id'));
|
||||
}
|
||||
|
||||
if (e(Input::get('is_warranty')) == '') {
|
||||
$assetMaintenance->is_warranty = 0;
|
||||
} else {
|
||||
$assetMaintenance->is_warranty = e(Input::get('is_warranty'));
|
||||
}
|
||||
|
||||
if (e(Input::get('cost')) == '') {
|
||||
$assetMaintenance->cost = '';
|
||||
} else {
|
||||
$assetMaintenance->cost = Helper::ParseFloat(e(Input::get('cost')));
|
||||
}
|
||||
|
||||
if (e(Input::get('notes')) == '') {
|
||||
$assetMaintenance->notes = null;
|
||||
} else {
|
||||
$assetMaintenance->notes = e(Input::get('notes'));
|
||||
}
|
||||
|
||||
$asset = Asset::find(e(Input::get('asset_id')));
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = e(Input::get('asset_id'));
|
||||
$assetMaintenance->asset_maintenance_type = e(Input::get('asset_maintenance_type'));
|
||||
$assetMaintenance->title = e(Input::get('title'));
|
||||
$assetMaintenance->start_date = e(Input::get('start_date'));
|
||||
$assetMaintenance->completion_date = e(Input::get('completion_date'));
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
|
||||
if (( $assetMaintenance->completion_date == "" )
|
||||
|| ( $assetMaintenance->completion_date == "0000-00-00" )
|
||||
if (( $assetMaintenance->completion_date == null )
|
||||
) {
|
||||
$assetMaintenance->completion_date = null;
|
||||
if (( $assetMaintenance->asset_maintenance_time !== 0 )
|
||||
|| ( !is_null($assetMaintenance->asset_maintenance_time) )
|
||||
) {
|
||||
@@ -400,8 +244,7 @@ class AssetMaintenancesController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if (( $assetMaintenance->completion_date !== "" )
|
||||
&& ( $assetMaintenance->completion_date !== "0000-00-00" )
|
||||
if (( $assetMaintenance->completion_date !== null )
|
||||
&& ( $assetMaintenance->start_date !== "" )
|
||||
&& ( $assetMaintenance->start_date !== "0000-00-00" )
|
||||
) {
|
||||
@@ -414,12 +257,10 @@ class AssetMaintenancesController extends Controller
|
||||
if ($assetMaintenance->save()) {
|
||||
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->to("admin/asset_maintenances")
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.edit.success'));
|
||||
}
|
||||
return redirect()->back() ->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,12 +272,12 @@ class AssetMaintenancesController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDelete($assetMaintenanceId)
|
||||
public function destroy($assetMaintenanceId)
|
||||
{
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->to('admin/asset_maintenances')
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
@@ -446,7 +287,7 @@ class AssetMaintenancesController extends Controller
|
||||
$assetMaintenance->delete();
|
||||
|
||||
// Redirect to the asset_maintenance management page
|
||||
return redirect()->to('admin/asset_maintenances')
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.delete.success'));
|
||||
}
|
||||
|
||||
@@ -459,17 +300,17 @@ class AssetMaintenancesController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getView($assetMaintenanceId)
|
||||
public function show($assetMaintenanceId)
|
||||
{
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->to('admin/asset_maintenances')
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
return View::make('asset_maintenances/view')->with('assetMaintenance', $assetMaintenance);
|
||||
return view('asset_maintenances/view')->with('assetMaintenance', $assetMaintenance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Image;
|
||||
use Input;
|
||||
use Lang;
|
||||
@@ -15,6 +16,8 @@ use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use Config;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
@@ -32,14 +35,13 @@ 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 getIndex()
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return View::make('models/index');
|
||||
$this->authorize('index', AssetModel::class);
|
||||
return view('models/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,16 +51,12 @@ class AssetModelsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$depreciation_list = Helper::depreciationList();
|
||||
$manufacturer_list = Helper::manufacturerList();
|
||||
$category_list = Helper::categoryList('asset');
|
||||
return View::make('models/edit')
|
||||
->with('category_list', $category_list)
|
||||
->with('depreciation_list', $depreciation_list)
|
||||
->with('manufacturer_list', $manufacturer_list)
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$category_type = 'asset';
|
||||
return view('models/edit')->with('category_type',$category_type)
|
||||
->with('depreciation_list', Helper::depreciationList())
|
||||
->with('item', new AssetModel);
|
||||
}
|
||||
|
||||
@@ -70,98 +68,58 @@ class AssetModelsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Create a new asset model
|
||||
$model = new AssetModel;
|
||||
|
||||
|
||||
if (e(Input::get('depreciation_id')) == '') {
|
||||
$model->depreciation_id = 0;
|
||||
} else {
|
||||
$model->depreciation_id = e(Input::get('depreciation_id'));
|
||||
}
|
||||
|
||||
if (e(Input::get('eol')) == '') {
|
||||
$model->eol = 0;
|
||||
} else {
|
||||
$model->eol = e(Input::get('eol'));
|
||||
}
|
||||
|
||||
// Save the model data
|
||||
$model->name = e(Input::get('name'));
|
||||
$model->model_number = e(Input::get('model_number'));
|
||||
$model->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$model->category_id = e(Input::get('category_id'));
|
||||
$model->notes = e(Input::get('notes'));
|
||||
$model->user_id = Auth::user()->id;
|
||||
$model->eol = $request->input('eol');
|
||||
$model->depreciation_id = $request->input('depreciation_id');
|
||||
$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->user_id = Auth::id();
|
||||
$model->requestable = Input::has('requestable');
|
||||
|
||||
if (Input::get('custom_fieldset')!='') {
|
||||
$model->fieldset_id = e(Input::get('custom_fieldset'));
|
||||
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);
|
||||
$file_name = str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
$path = app('models_upload_path');
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path.'/'.$file_name);
|
||||
} else {
|
||||
$image->move($path, $file_name);
|
||||
}
|
||||
$model->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
// 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()->to("hardware/models")->with('success', trans('admin/models/message.create.success'));
|
||||
return redirect()->route("models.index")->with('success', trans('admin/models/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($model->getErrors());
|
||||
|
||||
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]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
//COPYPASTA!!!! FIXME
|
||||
$model = new AssetModel;
|
||||
|
||||
$settings=Input::all();
|
||||
$settings['eol']= null;
|
||||
|
||||
$model->name=e(Input::get('name'));
|
||||
$model->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$model->category_id = e(Input::get('category_id'));
|
||||
$model->model_number = e(Input::get('model_number'));
|
||||
$model->user_id = Auth::user()->id;
|
||||
$model->note = e(Input::get('note'));
|
||||
$model->eol= null;
|
||||
|
||||
if (Input::get('fieldset_id')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = e(Input::get('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.
|
||||
*
|
||||
@@ -170,23 +128,18 @@ class AssetModelsController extends Controller
|
||||
* @param int $modelId
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($modelId = null)
|
||||
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()->to('assets/models')->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;
|
||||
}
|
||||
|
||||
$depreciation_list = Helper::depreciationList();
|
||||
$manufacturer_list = Helper::manufacturerList();
|
||||
$category_list = Helper::categoryList('asset');
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
|
||||
$view = View::make('models/edit', compact('item'));
|
||||
$view->with('category_list', $category_list);
|
||||
$view->with('depreciation_list', $depreciation_list);
|
||||
$view->with('manufacturer_list', $manufacturer_list);
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,68 +152,72 @@ class AssetModelsController extends Controller
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($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()->to('admin/models')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
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->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');
|
||||
|
||||
if (e(Input::get('depreciation_id')) == '') {
|
||||
$model->depreciation_id = 0;
|
||||
} else {
|
||||
$model->depreciation_id = e(Input::get('depreciation_id'));
|
||||
}
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if (e(Input::get('eol')) == '') {
|
||||
$model->eol = null;
|
||||
} else {
|
||||
$model->eol = e(Input::get('eol'));
|
||||
}
|
||||
// Update the model data
|
||||
$model->name = e(Input::get('name'));
|
||||
$model->model_number = e(Input::get('model_number'));
|
||||
$model->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$model->category_id = e(Input::get('category_id'));
|
||||
$model->notes = e(Input::get('notes'));
|
||||
|
||||
$model->requestable = Input::has('requestable');
|
||||
|
||||
if (Input::get('custom_fieldset')=='') {
|
||||
if ($request->input('custom_fieldset')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = e(Input::get('custom_fieldset'));
|
||||
$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;
|
||||
}
|
||||
$old_image = $model->image;
|
||||
|
||||
if (Input::get('image_delete') == 1 && Input::file('image') == "") {
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$model->image = null;
|
||||
}
|
||||
|
||||
// Was it created?
|
||||
if ($model->save()) {
|
||||
// Redirect to the new model page
|
||||
return redirect()->to("hardware/models")->with('success', trans('admin/models/message.update.success'));
|
||||
} else {
|
||||
return redirect()->back()->withInput()->withErrors($model->getErrors());
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $model->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('models_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('models_upload_path'), $file_name);
|
||||
}
|
||||
$model->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('models_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Redirect to the model create page
|
||||
return redirect()->to("hardware/models/$modelId/edit")->with('error', trans('admin/models/message.update.error'));
|
||||
|
||||
if ($model->save()) {
|
||||
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($model->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,25 +229,32 @@ class AssetModelsController extends Controller
|
||||
* @param int $modelId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($modelId)
|
||||
public function destroy($modelId)
|
||||
{
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model = AssetModel::find($modelId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('hardware/models')->with('error', trans('admin/models/message.not_found'));
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found'));
|
||||
}
|
||||
|
||||
if ($model->assets->count() > 0) {
|
||||
if ($model->assets()->count() > 0) {
|
||||
// Throw an error that this model is associated with assets
|
||||
return redirect()->to('hardware/models')->with('error', trans('admin/models/message.assoc_users'));
|
||||
|
||||
} else {
|
||||
// Delete the model
|
||||
$model->delete();
|
||||
|
||||
// Redirect to the models management page
|
||||
return redirect()->to('hardware/models')->with('success', trans('admin/models/message.delete.success'));
|
||||
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();
|
||||
|
||||
// Redirect to the models management page
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +268,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function getRestore($modelId = null)
|
||||
{
|
||||
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Get user information
|
||||
$model = AssetModel::withTrashed()->find($modelId);
|
||||
|
||||
@@ -317,11 +281,10 @@ class AssetModelsController extends Controller
|
||||
$success = trans('admin/models/message.restore.success');
|
||||
|
||||
// Redirect back
|
||||
return redirect()->back()->with('success', $success);
|
||||
return redirect()->route('models.index')->with('success', $success);
|
||||
|
||||
} else {
|
||||
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
|
||||
|
||||
}
|
||||
|
||||
@@ -334,21 +297,19 @@ class AssetModelsController extends Controller
|
||||
* @param int $modelId
|
||||
* @return View
|
||||
*/
|
||||
public function getView($modelId = null)
|
||||
public function show($modelId = null)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$model = AssetModel::withTrashed()->find($modelId);
|
||||
|
||||
if (isset($model->id)) {
|
||||
return View::make('models/view', compact('model'));
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/models/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('models')->with('error', $error);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,21 +324,15 @@ class AssetModelsController extends Controller
|
||||
{
|
||||
// Check if the model exists
|
||||
if (is_null($model_to_clone = AssetModel::find($modelId))) {
|
||||
// Redirect to the model management page
|
||||
return redirect()->to('assets/models')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$model = clone $model_to_clone;
|
||||
$model->id = null;
|
||||
|
||||
// Show the page
|
||||
$depreciation_list = Helper::depreciationList();
|
||||
$manufacturer_list = Helper::manufacturerList();
|
||||
$category_list = Helper::categoryList('asset');
|
||||
$view = View::make('models/edit');
|
||||
$view->with('category_list', $category_list);
|
||||
$view->with('depreciation_list', $depreciation_list);
|
||||
$view->with('manufacturer_list', $manufacturer_list);
|
||||
$view->with('depreciation_list', Helper::depreciationList());
|
||||
$view->with('item', $model);
|
||||
$view->with('clone_model', $model_to_clone);
|
||||
return $view;
|
||||
@@ -396,156 +351,186 @@ class AssetModelsController extends Controller
|
||||
public function getCustomFields($modelId)
|
||||
{
|
||||
$model = AssetModel::find($modelId);
|
||||
return View::make("models.custom_fields_form")->with("model", $model);
|
||||
return view("models.custom_fields_form")->with("model", $model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the JSON response to populate the data tables on the
|
||||
* Asset Model listing page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @param string $status
|
||||
* @return String JSON
|
||||
*/
|
||||
|
||||
public function getDatatable($status = null)
|
||||
* Returns a view that allows the user to bulk edit model attrbutes
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.7]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
$models = AssetModel::with('category', 'assets', 'depreciation', 'manufacturer');
|
||||
|
||||
switch ($status) {
|
||||
case 'Deleted':
|
||||
$models->withTrashed()->Deleted();
|
||||
break;
|
||||
}
|
||||
$models_raw_array = Input::get('ids');
|
||||
|
||||
// Make sure some IDs have been selected
|
||||
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
|
||||
|
||||
|
||||
if (Input::has('search')) {
|
||||
$models = $models->TextSearch(Input::get('search'));
|
||||
}
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->orderBy('assets_count', 'ASC')->get();
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
// 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);
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
|
||||
$allowed_columns = ['id','name','model_number'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
|
||||
|
||||
$models = $models->orderBy($sort, $order);
|
||||
|
||||
$modelCount = $models->count();
|
||||
$models = $models->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($models as $model) {
|
||||
if ($model->deleted_at == '') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('clone/model', $model->id).'" class="btn btn-info btn-sm" title="Clone Model" data-toggle="tooltip"><i class="fa fa-clone"></i></a> <a href="'.route('update/model', $model->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/model', $model->id).'" data-content="'.trans('admin/models/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($model->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
// Otherwise display the bulk edit screen
|
||||
} else {
|
||||
$actions = '<a href="'.route('restore/model', $model->id).'" class="btn btn-warning btn-sm"><i class="fa fa-recycle icon-white"></i></a>';
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $model->id,
|
||||
'manufacturer' => (string)link_to('/admin/settings/manufacturers/'.$model->manufacturer->id.'/view', $model->manufacturer->name),
|
||||
'name' => (string)link_to('/hardware/models/'.$model->id.'/view', $model->name),
|
||||
'image' => ($model->image!='') ? '<img src="'.config('app.url').'/uploads/models/'.$model->image.'" height=50 width=50>' : '',
|
||||
'modelnumber' => $model->model_number,
|
||||
'numassets' => $model->assets->count(),
|
||||
'depreciation' => (($model->depreciation) && ($model->depreciation->id > 0)) ? $model->depreciation->name.' ('.$model->depreciation->months.')' : trans('general.no_depreciation'),
|
||||
'category' => ($model->category) ? (string)link_to('admin/settings/categories/'.$model->category->id.'/view', $model->category->name) : '',
|
||||
'eol' => ($model->eol) ? $model->eol.' '.trans('general.months') : '',
|
||||
'note' => $model->getNote(),
|
||||
'fieldset' => ($model->fieldset) ? (string)link_to('admin/custom_fields/'.$model->fieldset->id, $model->fieldset->name) : '',
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $modelCount, 'rows' => $rows);
|
||||
return redirect()->route('models.index')
|
||||
->with('error', 'You must select at least one model to edit.');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the asset information to present to the model view detail page
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @param int $modelId
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDataView($modelID)
|
||||
* Returns a view that allows the user to bulk edit model attrbutes
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.7]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function postBulkEditSave(Request $request)
|
||||
{
|
||||
$assets = Asset::where('model_id', '=', $modelID)->with('company', 'assetstatus');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$assets = $assets->TextSearch(e(Input::get('search')));
|
||||
$models_raw_array = Input::get('ids');
|
||||
$update_array = array();
|
||||
|
||||
|
||||
if (($request->filled('manufacturer_id') && ($request->input('manufacturer_id')!='NC'))) {
|
||||
$update_array['manufacturer_id'] = $request->input('manufacturer_id');
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
if (($request->filled('category_id') && ($request->input('category_id')!='NC'))) {
|
||||
$update_array['category_id'] = $request->input('category_id');
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
if ($request->input('fieldset_id')!='NC') {
|
||||
$update_array['fieldset_id'] = $request->input('fieldset_id');
|
||||
}
|
||||
if ($request->input('depreciation_id')!='NC') {
|
||||
$update_array['depreciation_id'] = $request->input('depreciation_id');
|
||||
}
|
||||
|
||||
|
||||
$allowed_columns = ['name', 'serial','asset_tag'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
|
||||
|
||||
if (count($update_array) > 0) {
|
||||
AssetModel::whereIn('id', $models_raw_array)->update($update_array);
|
||||
return redirect()->route('models.index')
|
||||
->with('success', trans('admin/models/message.bulkedit.success'));
|
||||
}
|
||||
|
||||
$assets = $assets->orderBy($sort, $order);
|
||||
return redirect()->route('models.index')
|
||||
->with('warning', trans('admin/models/message.bulkedit.error'));
|
||||
|
||||
$assetsCount = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
/**
|
||||
* 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)) {
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$actions = '';
|
||||
$models = AssetModel::whereIn('id', $models_raw_array)->withCount('assets as assets_count')->get();
|
||||
|
||||
if ($asset->assetstatus) {
|
||||
if ($asset->assetstatus->deployable != 0) {
|
||||
if (($asset->assigned_to !='') && ($asset->assigned_to > 0)) {
|
||||
$actions = '<a href="'.route('checkin/hardware', $asset->id).'" class="btn btn-primary btn-sm">'.trans('general.checkin').'</a>';
|
||||
} else {
|
||||
$actions = '<a href="'.route('checkout/hardware', $asset->id).'" class="btn btn-info btn-sm">'.trans('general.checkout').'</a>';
|
||||
}
|
||||
$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++;
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string)link_to('/hardware/'.$asset->id.'/view', $asset->showAssetName()),
|
||||
'asset_tag' => (string)link_to('hardware/'.$asset->id.'/view', $asset->asset_tag),
|
||||
'serial' => $asset->serial,
|
||||
'assigned_to' => ($asset->assigned_to) ? (string)link_to('/admin/users/'.$asset->assigned_to.'/view', $asset->assigneduser->fullName()) : '',
|
||||
'actions' => $actions,
|
||||
'companyName' => Company::getName($asset)
|
||||
);
|
||||
\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]));
|
||||
}
|
||||
|
||||
$data = array('total' => $assetsCount, 'rows' => $rows);
|
||||
return redirect()->route('models.index')
|
||||
->with('error', trans('admin/models/message.bulkdelete.error'));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
@@ -1,296 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Validator;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Config;
|
||||
use Illuminate\Http\Request;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Log;
|
||||
use View;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This controller handles authentication for the user, including local
|
||||
* database users and LDAP users.
|
||||
*
|
||||
* @todo Move LDAP methods into user model for better separation of concerns.
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @version v1.0
|
||||
*/
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
|
||||
|
||||
// This tells the auth controller to use username instead of email address
|
||||
protected $username = 'username';
|
||||
|
||||
/**
|
||||
* Where to redirect users after login / registration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
|
||||
}
|
||||
|
||||
|
||||
function showLoginForm()
|
||||
{
|
||||
if (Auth::check()) {
|
||||
return redirect()->intended('dashboard');
|
||||
}
|
||||
return View::make('auth.login');
|
||||
}
|
||||
|
||||
|
||||
private function login_via_ldap(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");
|
||||
throw new \Exception("Could not find user in LDAP directory");
|
||||
} else {
|
||||
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");
|
||||
|
||||
// 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'));
|
||||
|
||||
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
|
||||
LOG::debug("Local user created.");
|
||||
} else {
|
||||
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.");
|
||||
|
||||
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
|
||||
|
||||
if (Setting::getSettings()->ldap_pw_sync=='1') {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
$user->email = $ldap_attr['email'];
|
||||
$user->first_name = $ldap_attr['firstname'];
|
||||
$user->last_name = $ldap_attr['lastname'];
|
||||
$user->save();
|
||||
} // End if(!user)
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Account sign in form processing.
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$validator = $this->validator(Input::all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
$throttles = $this->isUsingThrottlesLoginsTrait();
|
||||
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
|
||||
$this->lockoutTime = config('auth.throttle.lockout_duration');
|
||||
|
||||
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
|
||||
return $this->sendLockoutResponse($request);
|
||||
}
|
||||
|
||||
$user = null;
|
||||
|
||||
// Should we even check for LDAP users?
|
||||
if (Setting::getSettings()->ldap_enabled=='1') {
|
||||
LOG::debug("LDAP is enabled.");
|
||||
try {
|
||||
$user = $this->login_via_ldap($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());
|
||||
}
|
||||
}
|
||||
|
||||
// If the user wasn't authenticated via LDAP, skip to local auth
|
||||
if(!$user) {
|
||||
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 ($throttles && ! $lockedOut) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
}
|
||||
|
||||
LOG::debug("Local authentication failed.");
|
||||
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
|
||||
} else {
|
||||
if ($throttles) {
|
||||
$this->clearLoginAttempts($request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the page we were before
|
||||
$redirect = \Session::get('loginRedirect', 'home');
|
||||
|
||||
// Unset the page we were before from the session
|
||||
\Session::forget('loginRedirect');
|
||||
|
||||
// Redirect to the users page
|
||||
return redirect()->to($redirect)->with('success', trans('auth/message.signin.success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two factor enrollment page
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getTwoFactorEnroll()
|
||||
{
|
||||
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
|
||||
if ($user->two_factor_secret=='') {
|
||||
$user->two_factor_secret = $google2fa->generateSecretKey(32);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
||||
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
|
||||
urlencode(Setting::getSettings()->site_name),
|
||||
urlencode($user->username),
|
||||
$user->two_factor_secret
|
||||
);
|
||||
|
||||
return View::make('auth.two_factor_enroll')->with('google2fa_url',$google2fa_url);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two factor code form page
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getTwoFactorAuth() {
|
||||
return View::make('auth.two_factor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Two factor code submission
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postTwoFactorAuth(Request $request) {
|
||||
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$secret = $request->get('two_factor_secret');
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);
|
||||
|
||||
if ($valid) {
|
||||
$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');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logout page.
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->session()->forget('2fa_authed');
|
||||
Auth::logout();
|
||||
return redirect()->route('login')->with('success', 'You have successfully logged out!');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the login lockout error message.
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return string
|
||||
*/
|
||||
protected function getLockoutErrorMessage($seconds)
|
||||
{
|
||||
return \Lang::has('auth/message.throttle')
|
||||
? \Lang::get('auth/message.throttle', ['seconds' => $seconds])
|
||||
: 'Too many login attempts. Please try again in '.$seconds.' seconds.';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
75
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
75
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset emails and
|
||||
| includes a trait which assists in sending these notifications from
|
||||
| your application to your users. Feel free to explore this trait.
|
||||
|
|
||||
*/
|
||||
|
||||
use SendsPasswordResetEmails;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the e-mail subject line to be used for the reset link email.
|
||||
* Overriding method "getEmailSubject()" from trait "use ResetsPasswords"
|
||||
* @return string
|
||||
*/
|
||||
public function getEmailSubject()
|
||||
{
|
||||
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
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.
|
||||
$response = $this->broker()->sendResetLink(
|
||||
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.
|
||||
return back()->withErrors(
|
||||
['email' => trans($response)]
|
||||
);
|
||||
}
|
||||
}
|
||||
389
app/Http/Controllers/Auth/LoginController.php
Normal file
389
app/Http/Controllers/Auth/LoginController.php
Normal file
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Validator;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Config;
|
||||
use Illuminate\Http\Request;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Log;
|
||||
use View;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
|
||||
/**
|
||||
* This controller handles authentication for the user, including local
|
||||
* database users and LDAP users.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @version v1.0
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
|
||||
use ThrottlesLogins;
|
||||
|
||||
// This tells the auth controller to use username instead of email address
|
||||
protected $username = 'username';
|
||||
|
||||
/**
|
||||
* Where to redirect users after login / registration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
|
||||
\Session::put('backUrl', \URL::previous());
|
||||
}
|
||||
|
||||
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 loginViaRemoteUser(Request $request)
|
||||
{
|
||||
$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");
|
||||
throw new \Exception("Could not find user in LDAP directory");
|
||||
} else {
|
||||
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)->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'));
|
||||
|
||||
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
|
||||
Log::debug("Local user created.");
|
||||
} else {
|
||||
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.");
|
||||
|
||||
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
|
||||
|
||||
if (Setting::getSettings()->ldap_pw_sync=='1') {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
||||
$user->email = $ldap_attr['email'];
|
||||
$user->first_name = $ldap_attr['firstname'];
|
||||
$user->last_name = $ldap_attr['lastname'];
|
||||
$user->save();
|
||||
} // End if(!user)
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Account sign in form processing.
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
if (Setting::getSettings()->login_common_disabled == "1") {
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
$validator = $this->validator(Input::all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
|
||||
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
|
||||
$this->lockoutTime = config('auth.throttle.lockout_duration');
|
||||
|
||||
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
return $this->sendLockoutResponse($request);
|
||||
}
|
||||
|
||||
$user = null;
|
||||
|
||||
// Should we even check for LDAP users?
|
||||
if (Setting::getSettings()->ldap_enabled=='1') {
|
||||
Log::debug("LDAP is enabled.");
|
||||
try {
|
||||
$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::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.");
|
||||
// Try to log the user in
|
||||
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.");
|
||||
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
|
||||
} else {
|
||||
|
||||
$this->clearLoginAttempts($request);
|
||||
}
|
||||
}
|
||||
|
||||
if ($user = Auth::user()) {
|
||||
$user->last_login = \Carbon::now();
|
||||
$user->save();
|
||||
}
|
||||
// Redirect to the users page
|
||||
return redirect()->intended()->with('success', trans('auth/message.signin.success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two factor enrollment page
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getTwoFactorEnroll()
|
||||
{
|
||||
|
||||
// Make sure the user is logged in
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$user = Auth::user();
|
||||
|
||||
// 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();
|
||||
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two factor code form page
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Two factor code submission
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postTwoFactorAuth(Request $request)
|
||||
{
|
||||
|
||||
if (!Auth::check()) {
|
||||
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();
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $request->input('two_factor_secret');
|
||||
|
||||
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', trans('auth/message.two_factor.invalid_code'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logout page.
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->session()->forget('2fa_authed');
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$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'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function username()
|
||||
{
|
||||
return 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
$this->throttleKey($request)
|
||||
);
|
||||
|
||||
$minutes = round($seconds / 60);
|
||||
|
||||
$message = \Lang::get('auth/message.throttle', ['minutes' => $minutes]);
|
||||
|
||||
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 bool
|
||||
*/
|
||||
protected function hasTooManyLoginAttempts(Request $request)
|
||||
{
|
||||
$lockoutTime = config('auth.throttle.lockout_duration');
|
||||
$maxLoginAttempts = config('auth.throttle.max_attempts');
|
||||
|
||||
return $this->limiter()->tooManyAttempts(
|
||||
$this->throttleKey($request),
|
||||
$maxLoginAttempts,
|
||||
$lockoutTime
|
||||
);
|
||||
}
|
||||
|
||||
public function legacyAuthRedirect() {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
public function redirectTo()
|
||||
{
|
||||
return Session::get('backUrl') ? Session::get('backUrl') : $this->redirectTo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
/**
|
||||
* This controller handles password resets for the user.
|
||||
* It uses the built-in Laravel magic of ResetsPasswords, so we
|
||||
* don't actually do much here.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @version v1.0
|
||||
*/
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Create a new password controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the e-mail subject line to be used for the reset link email.
|
||||
* Overriding method "getEmailSubject()" from trait "use ResetsPasswords"
|
||||
* @return string
|
||||
*/
|
||||
public function getEmailSubject(){
|
||||
return property_exists($this, 'subject') ? $this->subject : \Lang::get('mail.reset_link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for after the reset link has been successfully sent.
|
||||
*
|
||||
* @param string $response
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
protected function getSendResetLinkEmailSuccessResponse($response)
|
||||
{
|
||||
return redirect()->route('login')->with('status', trans($response));
|
||||
}
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
||||
45
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
45
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Where to redirect users after resetting their password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Category as Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use Auth;
|
||||
use DB;
|
||||
@@ -14,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
|
||||
@@ -32,12 +35,13 @@ class CategoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return View::make('categories/index');
|
||||
$this->authorize('view', Category::class);
|
||||
return view('categories/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -45,16 +49,17 @@ class CategoriesController extends Controller
|
||||
* Returns a form view to create a new category.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::postCreate() method that stores the data
|
||||
* @see CategoriesController::store() method that stores the data
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$this->authorize('create', Category::class);
|
||||
$category_types= Helper::categoryTypeList();
|
||||
return View::make('categories/edit')->with('item', new Category)
|
||||
->with('category_types', $category_types);
|
||||
return view('categories/edit')->with('item', new Category)
|
||||
->with('category_types', $category_types);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,39 +67,39 @@ class CategoriesController extends Controller
|
||||
* Validates and stores the new category data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::getCreate() method that makes the form.
|
||||
* @see CategoriesController::create() method that makes the form.
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
// create a new model instance
|
||||
$this->authorize('create', Category::class);
|
||||
$category = new Category();
|
||||
$category->name = $request->input('name');
|
||||
$category->category_type = $request->input('category_type');
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->user_id = Auth::id();
|
||||
|
||||
// Update the category data
|
||||
$category->name = e(Input::get('name'));
|
||||
$category->category_type = e(Input::get('category_type'));
|
||||
$category->eula_text = e(Input::get('eula_text'));
|
||||
$category->use_default_eula = e(Input::get('use_default_eula', '0'));
|
||||
$category->require_acceptance = e(Input::get('require_acceptance', '0'));
|
||||
$category->checkin_email = e(Input::get('checkin_email', '0'));
|
||||
$category->user_id = Auth::user()->id;
|
||||
|
||||
if ($category->save()) {
|
||||
// Redirect to the new category page
|
||||
return redirect()->to("admin/settings/categories")->with('success', trans('admin/categories/message.create.success'));
|
||||
} else {
|
||||
|
||||
// The given data did not pass validation
|
||||
return redirect()->back()->withInput()->withErrors($category->getErrors());
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/categories/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$category->image = $file_name;
|
||||
}
|
||||
|
||||
// Redirect to the category create page
|
||||
return redirect()->to('admin/settings/categories/create')->with('error', trans('admin/categories/message.create.error'));
|
||||
|
||||
if ($category->save()) {
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($category->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,67 +109,87 @@ class CategoriesController extends Controller
|
||||
* @see CategoriesController::postEdit() method saves the data
|
||||
* @param int $categoryId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($categoryId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
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'));
|
||||
}
|
||||
|
||||
// Show the page
|
||||
//$category_options = array('' => 'Top Level') + Category::lists('name', 'id');
|
||||
|
||||
$category_options = array('' => 'Top Level') + DB::table('categories')->where('id', '!=', $categoryId)->lists('name', 'id');
|
||||
$category_types= Helper::categoryTypeList();
|
||||
|
||||
return View::make('categories/edit', compact('item'))
|
||||
->with('category_options', $category_options)
|
||||
return view('categories/edit', compact('item'))
|
||||
->with('category_types', $category_types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the updated category data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::getEdit() method that makes the form.
|
||||
* @param int $categoryId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit(Request $request, $categoryId = null)
|
||||
* Validates and stores the updated category data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::getEdit() method that makes the form.
|
||||
* @param Request $request
|
||||
* @param int $categoryId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
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 blogs management page
|
||||
// Redirect to the categories management page
|
||||
return redirect()->to('admin/categories')->with('error', trans('admin/categories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Update the category data
|
||||
$category->name = e($request->input('name'));
|
||||
$category->name = $request->input('name');
|
||||
// If the item count is > 0, we disable the category type in the edit. Disabled items
|
||||
// don't POST, so if the category_type is blank we just set it to the default.
|
||||
$category->category_type = e($request->input('category_type', $category->category_type));
|
||||
$category->eula_text = e($request->input('eula_text'));
|
||||
$category->use_default_eula = e($request->input('use_default_eula', '0'));
|
||||
$category->require_acceptance = e($request->input('require_acceptance', '0'));
|
||||
$category->checkin_email = e($request->input('checkin_email', '0'));
|
||||
$category->category_type = $request->input('category_type', $category->category_type);
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
|
||||
if ($category->save()) {
|
||||
// Redirect to the new category page
|
||||
return redirect()->to("admin/settings/categories")->with('success', trans('admin/categories/message.update.success'));
|
||||
} // attempt validation
|
||||
else {
|
||||
// The given data did not pass validation
|
||||
return redirect()->back()->withInput()->withErrors($category->getErrors());
|
||||
$old_image = $category->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$category->image = null;
|
||||
}
|
||||
|
||||
// Redirect to the category management page
|
||||
return redirect()->back()->with('error', trans('admin/categories/message.update.error'));
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $category->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('categories_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('categories_upload_path'), $file_name);
|
||||
}
|
||||
$category->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('categories_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($category->save()) {
|
||||
// Redirect to the new category page
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.update.success'));
|
||||
}
|
||||
// The given data did not pass validation
|
||||
return redirect()->back()->withInput()->withErrors($category->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,41 +198,32 @@ class CategoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $categoryId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($categoryId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($categoryId)
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::find($categoryId))) {
|
||||
// Redirect to the blogs management page
|
||||
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']));
|
||||
} else {
|
||||
|
||||
$category->delete();
|
||||
|
||||
// Redirect to the locations management page
|
||||
return redirect()->to('admin/settings/categories')->with('success', trans('admin/categories/message.delete.success'));
|
||||
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()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the categories detail view, which is generated in getDataView.
|
||||
@@ -216,319 +232,33 @@ class CategoriesController extends Controller
|
||||
* @see CategoriesController::getDataView() method that generates the JSON response
|
||||
* @param int $categoryId
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getView($categoryId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$category = Category::find($categoryId);
|
||||
$this->authorize('view', Category::class);
|
||||
if ($category = Category::find($id)) {
|
||||
|
||||
if (isset($category->id)) {
|
||||
return View::make('categories/view', compact('category'));
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/categories/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('categories')->with('error', $error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON response with the data to populate the bootstrap table on the
|
||||
* cateory listing page.
|
||||
*
|
||||
* @todo Refactor this nastiness. Assets do not behave the same as accessories, etc.
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see CategoriesController::getIndex() method that generates the view
|
||||
* @since [v1.8]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable()
|
||||
{
|
||||
// Grab all the categories
|
||||
$categories = Category::with('assets', 'accessories', 'consumables', 'components');
|
||||
|
||||
if (Input::has('search')) {
|
||||
$categories = $categories->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
|
||||
$allowed_columns = ['id','name','category_type'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? e(Input::get('sort')) : 'created_at';
|
||||
|
||||
$categories = $categories->orderBy($sort, $order);
|
||||
|
||||
$catCount = $categories->count();
|
||||
$categories = $categories->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
|
||||
$actions = '<a href="'.route('update/category', $category->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;">';
|
||||
$actions .='<i class="fa fa-pencil icon-white"></i></a>';
|
||||
$actions .='<a data-html="false" class="btn delete-asset btn-danger btn-sm';
|
||||
if ($category->itemCount() > 0) {
|
||||
$actions .=' disabled';
|
||||
}
|
||||
$actions .=' data-toggle="modal" href="'.route('delete/category', $category->id).'" data-content="'.trans('admin/categories/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($category->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
$rows[] = array(
|
||||
'id' => $category->id,
|
||||
'name' => (string)link_to('/admin/settings/categories/'.$category->id.'/view', $category->name) ,
|
||||
'category_type' => ucwords($category->category_type),
|
||||
'count' => $category->itemCount(),
|
||||
'acceptance' => ($category->require_acceptance=='1') ? '<i class="fa fa-check"></i>' : '',
|
||||
'eula' => ($category->getEula()) ? '<i class="fa fa-check"></i>' : '',
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $catCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataViewAssets($categoryID)
|
||||
{
|
||||
|
||||
$category = Category::find($categoryID);
|
||||
$category = $category->load('assets.company', 'assets.model', 'assets.assetstatus', 'assets.assigneduser');
|
||||
$category_assets = $category->assets();
|
||||
if (Input::has('search')) {
|
||||
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$count = $category_assets->count();
|
||||
$category_assets = $category_assets->skip($offset)->take($limit)->get();
|
||||
$rows = array();
|
||||
foreach ($category_assets as $asset) {
|
||||
|
||||
$actions = '';
|
||||
$inout='';
|
||||
|
||||
if ($asset->deleted_at=='') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('clone/hardware', $asset->id).'" class="btn btn-info btn-sm" title="Clone asset"><i class="fa fa-files-o"></i></a> <a href="'.route('update/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/hardware', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->asset_tag).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
} elseif ($asset->deleted_at!='') {
|
||||
$actions = '<a href="'.route('restore/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-recycle icon-white"></i></a>';
|
||||
}
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
if (Gate::allows('assets.checkout')) {
|
||||
$inout = '<a href="'.route('checkout/hardware', $asset->id).'" class="btn btn-info btn-sm">'.trans('general.checkout').'</a>';
|
||||
}
|
||||
if ($category->category_type=='asset') {
|
||||
$category_type = 'hardware';
|
||||
$category_type_route = 'assets';
|
||||
} elseif ($category->category_type=='accessory') {
|
||||
$category_type = 'accessories';
|
||||
$category_type_route = 'accessories';
|
||||
} else {
|
||||
if (Gate::allows('assets.checkin')) {
|
||||
$inout = '<a href="'.route('checkin/hardware', $asset->id).'" class="btn btn-primary btn-sm">'.trans('general.checkin').'</a>';
|
||||
}
|
||||
$category_type = $category->category_type;
|
||||
$category_type_route = $category->category_type.'s';
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string)link_to('/hardware/'.$asset->id.'/view', $asset->showAssetName()),
|
||||
'model' => ($asset->model) ? (string)link_to('hardware/models/'.$asset->model->id.'/view', $asset->model->name) : '',
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
'serial' => $asset->serial,
|
||||
'assigned_to' => ($asset->assigneduser) ? (string)link_to('/admin/users/'.$asset->assigneduser->id.'/view', $asset->assigneduser->fullName()): '',
|
||||
'change' => $inout,
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($asset->company) ? '' : e($asset->company->name)
|
||||
);
|
||||
return view('categories/view', compact('category'))
|
||||
->with('category_type',$category_type)
|
||||
->with('category_type_route',$category_type_route);
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getDataViewAccessories($categoryID)
|
||||
{
|
||||
|
||||
$category = Category::with('accessories.company')->find($categoryID);
|
||||
$category_assets = $category->accessories;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$count = $category_assets->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($category_assets as $asset) {
|
||||
|
||||
$actions = '';
|
||||
$inout='';
|
||||
|
||||
if ($asset->deleted_at=='') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/accessory', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/accessory', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string)link_to_route('view/accessory', $asset->name, [$asset->id]),
|
||||
'actions' => $actions,
|
||||
'companyName' => Company::getName($asset),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function getDataViewConsumables($categoryID)
|
||||
{
|
||||
|
||||
$category = Category::with('accessories.company')->find($categoryID);
|
||||
$category_assets = $category->consumables;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$count = $category_assets->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($category_assets as $asset) {
|
||||
|
||||
$actions = '';
|
||||
$inout='';
|
||||
|
||||
if ($asset->deleted_at=='') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/consumable', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/consumable', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string) link_to_route('view/consumable', $asset->name, [$asset->id]),
|
||||
'actions' => $actions,
|
||||
'companyName' => Company::getName($asset),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataViewComponent($categoryID)
|
||||
{
|
||||
|
||||
$category = Category::with('accessories.company')->find($categoryID);
|
||||
$category_assets = $category->components;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$category_assets = $category_assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$count = $category_assets->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($category_assets as $asset) {
|
||||
|
||||
$actions = '';
|
||||
$inout='';
|
||||
|
||||
if ($asset->deleted_at=='') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('update/component', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/component', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string)link_to_route('view/accessory', $asset->name, [$asset->id]),
|
||||
'actions' => $actions,
|
||||
'companyName' => Company::getName($asset),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ use Input;
|
||||
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
|
||||
@@ -22,11 +25,13 @@ final class CompaniesController extends Controller
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return View::make('companies/index')->with('companies', Company::all());
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
return view('companies/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,33 +39,46 @@ final class CompaniesController extends Controller
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return View::make('companies/edit')->with('item', new Company);
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
return view('companies/edit')->with('item', new Company);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data from new company form.
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* Save data from new company form.
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
$company = new Company;
|
||||
$company->name = $request->input('name');
|
||||
|
||||
$company->name = e(Input::get('name'));
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->to('admin/settings/companies')
|
||||
->with('success', trans('admin/companies/message.create.success'));
|
||||
} else {
|
||||
return redirect()->back()->withInput()->withErrors($company->getErrors());
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/companies/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$company->image = $file_name;
|
||||
}
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('success', trans('admin/companies/message.create.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($company->getErrors());
|
||||
}
|
||||
|
||||
|
||||
@@ -70,44 +88,77 @@ final class CompaniesController extends Controller
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param int $companyId
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($companyId)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($companyId)
|
||||
{
|
||||
if (is_null($item = Company::find($companyId))) {
|
||||
return redirect()->to('admin/settings/companies')
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
} else {
|
||||
return View::make('companies/edit')->with('item', $item);
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('companies/edit')->with('item', $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data from edit company form.
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param int $companyId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($companyId)
|
||||
* Save data from edit company form.
|
||||
*
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param Request $request
|
||||
* @param int $companyId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $companyId)
|
||||
{
|
||||
if (is_null($company = Company::find($companyId))) {
|
||||
return redirect()->to('admin/settings/companies')->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
} else {
|
||||
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $company);
|
||||
|
||||
$company->name = e(Input::get('name'));
|
||||
$company->name = $request->input('name');
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->to('admin/settings/companies')
|
||||
->with('success', trans('admin/companies/message.update.success'));
|
||||
$old_image = $company->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$company->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $company->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('companies_upload_path').$file_name);
|
||||
} else {
|
||||
return redirect()->to("admin/settings/companies/$companyId/edit")
|
||||
->with('error', trans('admin/companies/message.update.error'));
|
||||
$image->move(app('companies_upload_path'), $file_name);
|
||||
}
|
||||
$company->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('companies_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($company->save()) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('success', trans('admin/companies/message.update.success'));
|
||||
}
|
||||
return redirect()->route('companies.edit', ['company' => $companyId])
|
||||
->with('error', trans('admin/companies/message.update.error'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,17 +167,20 @@ final class CompaniesController extends Controller
|
||||
* @author [Abdullah Alansari] [<ahimta@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @param int $companyId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postDelete($companyId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($companyId)
|
||||
{
|
||||
if (is_null($company = Company::find($companyId))) {
|
||||
return redirect()->to('admin/settings/companies')
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.not_found'));
|
||||
} else {
|
||||
|
||||
$this->authorize('delete', $company);
|
||||
|
||||
try {
|
||||
$company->delete();
|
||||
return redirect()->to('admin/settings/companies')
|
||||
return redirect()->route('companies.index')
|
||||
->with('success', trans('admin/companies/message.delete.success'));
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
/*
|
||||
@@ -134,7 +188,7 @@ final class CompaniesController extends Controller
|
||||
* For example when rows in other tables are referencing this company
|
||||
*/
|
||||
if ($exception->getCode() == 23000) {
|
||||
return redirect()->to('admin/settings/companies')
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.assoc_users'));
|
||||
} else {
|
||||
throw $exception;
|
||||
@@ -142,4 +196,16 @@ final class CompaniesController extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id) {
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
if (is_null($company = Company::find($id))) {
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.not_found'));
|
||||
} else {
|
||||
return view('companies/view')->with('company',$company);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
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;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Models\Asset;
|
||||
@@ -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
|
||||
@@ -37,11 +40,12 @@ class ComponentsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getDatatable() method that generates the JSON response
|
||||
* @since [v3.0]
|
||||
* @return View
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getIndex()
|
||||
public function index()
|
||||
{
|
||||
return View::make('components/index');
|
||||
$this->authorize('view', Component::class);
|
||||
return view('components/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -51,20 +55,14 @@ class ComponentsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::postCreate() method that stores the data
|
||||
* @since [v3.0]
|
||||
* @return View
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getCreate()
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$category_list = Helper::categoryList('component');
|
||||
$company_list = Helper::companyList();
|
||||
$location_list = Helper::locationsList();
|
||||
|
||||
return View::make('components/edit')
|
||||
->with('item', new Component)
|
||||
->with('category_list', $category_list)
|
||||
->with('company_list', $company_list)
|
||||
->with('location_list', $location_list);
|
||||
$this->authorize('create', Component::class);
|
||||
$category_type = 'component';
|
||||
return view('components/edit')->with('category_type',$category_type)
|
||||
->with('item', new Component);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,48 +72,40 @@ class ComponentsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getCreate() method that generates the view
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
// create a new model instance
|
||||
$this->authorize('create', Component::class);
|
||||
$component = new Component();
|
||||
$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();
|
||||
|
||||
// Update the component data
|
||||
$component->name = e(Input::get('name'));
|
||||
$component->category_id = e(Input::get('category_id'));
|
||||
$component->location_id = e(Input::get('location_id'));
|
||||
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$component->order_number = e(Input::get('order_number'));
|
||||
$component->min_amt = e(Input::get('min_amt'));
|
||||
$component->serial = e(Input::get('serial'));
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$component->purchase_date = null;
|
||||
} else {
|
||||
$component->purchase_date = e(Input::get('purchase_date'));
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/components/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$component->image = $file_name;
|
||||
}
|
||||
|
||||
if (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$component->purchase_cost = null;
|
||||
} else {
|
||||
$component->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
|
||||
}
|
||||
|
||||
$component->qty = e(Input::get('qty'));
|
||||
$component->user_id = Auth::user()->id;
|
||||
|
||||
// Was the component created?
|
||||
if ($component->save()) {
|
||||
$component->logCreate();
|
||||
// Redirect to the new component page
|
||||
return redirect()->to("admin/components")->with('success', trans('admin/components/message.create.success'));
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,26 +115,22 @@ class ComponentsController extends Controller
|
||||
* @see ComponentsController::postEdit() method that stores the data.
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($componentId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($componentId = null)
|
||||
{
|
||||
// Check if the component exists
|
||||
if (is_null($item = Component::find($componentId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/components')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($item)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
|
||||
|
||||
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'));
|
||||
|
||||
|
||||
|
||||
$category_list = Helper::categoryList('component');
|
||||
$company_list = Helper::companyList();
|
||||
$location_list = Helper::locationsList();
|
||||
|
||||
return View::make('components/edit', compact('item'))
|
||||
->with('category_list', $category_list)
|
||||
->with('company_list', $company_list)
|
||||
->with('location_list', $location_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -155,53 +141,46 @@ class ComponentsController extends Controller
|
||||
* @see ComponentsController::getEdit() method presents the form.
|
||||
* @param int $componentId
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($componentId = null)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $componentId = null)
|
||||
{
|
||||
// Check if the blog post exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/components')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($component)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $component);
|
||||
|
||||
|
||||
// Update the component data
|
||||
$component->name = e(Input::get('name'));
|
||||
$component->category_id = e(Input::get('category_id'));
|
||||
$component->location_id = e(Input::get('location_id'));
|
||||
$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 = e(Input::get('order_number'));
|
||||
$component->min_amt = e(Input::get('min_amt'));
|
||||
$component->serial = e(Input::get('serial'));
|
||||
$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');
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$component->purchase_date = null;
|
||||
} else {
|
||||
$component->purchase_date = e(Input::get('purchase_date'));
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/components/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$component->image = $file_name;
|
||||
} elseif ($request->input('image_delete')=='1') {
|
||||
$component->image = null;
|
||||
}
|
||||
|
||||
if (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$component->purchase_cost = null;
|
||||
} else {
|
||||
$component->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
|
||||
}
|
||||
|
||||
$component->qty = e(Input::get('qty'));
|
||||
|
||||
// Was the component created?
|
||||
if ($component->save()) {
|
||||
// Redirect to the new component page
|
||||
return redirect()->to("admin/components")->with('success', trans('admin/components/message.update.success'));
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($component->getErrors());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,36 +189,19 @@ class ComponentsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($componentId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($componentId)
|
||||
{
|
||||
// Check if the blog post exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/components')->with('error', trans('admin/components/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($component)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$component->delete();
|
||||
|
||||
// Redirect to the locations management page
|
||||
return redirect()->to('admin/components')->with('success', trans('admin/components/message.delete.success'));
|
||||
|
||||
$this->authorize('delete', $component);
|
||||
$component->delete();
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
|
||||
}
|
||||
|
||||
public function postBulk($componentId = null)
|
||||
{
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
public function postBulkSave($componentId = null)
|
||||
{
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a view to display component information.
|
||||
*
|
||||
@@ -247,29 +209,20 @@ class ComponentsController extends Controller
|
||||
* @see ComponentsController::getDataView() method that generates the JSON response
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return View
|
||||
*/
|
||||
public function getView($componentId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($componentId = null)
|
||||
{
|
||||
$component = Component::find($componentId);
|
||||
|
||||
if (isset($component->id)) {
|
||||
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($component)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
} else {
|
||||
return View::make('components/view', compact('component'));
|
||||
}
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/components/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('components')->with('error', $error);
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,50 +232,41 @@ class ComponentsController extends Controller
|
||||
* @see ComponentsController::postCheckout() method that stores the data.
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return View
|
||||
*/
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getCheckout($componentId)
|
||||
{
|
||||
// Check if the component exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
// Redirect to the component management page with error
|
||||
return redirect()->to('components')->with('error', trans('admin/components/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($component)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
// Get the dropdown of assets and then pass it to the checkout view
|
||||
$assets_list = Helper::detailedAssetList();
|
||||
|
||||
return View::make('components/checkout', compact('component'))->with('assets_list', $assets_list);
|
||||
|
||||
$this->authorize('checkout', $component);
|
||||
return view('components/checkout', compact('component'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and store checkout data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getCheckout() method that returns the form.
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @return Redirect
|
||||
*/
|
||||
* Validate and store checkout data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentsController::getCheckout() method that returns the form.
|
||||
* @since [v3.0]
|
||||
* @param Request $request
|
||||
* @param int $componentId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postCheckout(Request $request, $componentId)
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Check if the component exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
// Redirect to the component management page with error
|
||||
return redirect()->to('components')->with('error', trans('admin/components/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($component)) {
|
||||
return redirect()->to('admin/components')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $component);
|
||||
|
||||
$max_to_checkout = $component->numRemaining();
|
||||
$validator = Validator::make($request->all(),[
|
||||
$validator = Validator::make($request->all(), [
|
||||
"asset_id" => "required",
|
||||
"assigned_qty" => "required|numeric|between:1,$max_to_checkout"
|
||||
]);
|
||||
@@ -339,190 +283,115 @@ class ComponentsController extends Controller
|
||||
// Check if the user exists
|
||||
if (is_null($asset = Asset::find($asset_id))) {
|
||||
// Redirect to the component management page with error
|
||||
return redirect()->to('admin/components')->with('error', trans('admin/components/message.asset_does_not_exist'));
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.asset_does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
// Update the component data
|
||||
$component->asset_id = $asset_id;
|
||||
|
||||
$component->assets()->attach($component->id, array(
|
||||
'component_id' => $component->id,
|
||||
'user_id' => $admin_user->id,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'assigned_qty' => e(Input::get('assigned_qty')),
|
||||
'asset_id' => $asset_id));
|
||||
|
||||
$logaction = $component->logCheckout(e(Input::get('note')), $asset_id);
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'Checked Out:',
|
||||
'value' => class_basename(strtoupper($logaction->item_type)).' <'.config('app.url').'/admin/components/'.$component->id.'/view'.'|'.$component->name.'> checked out to <'.config('app.url').'/hardware/'.$asset->id.'/view|'.$asset->showAssetName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
|
||||
],
|
||||
[
|
||||
'title' => 'Note:',
|
||||
'value' => e($logaction->note)
|
||||
],
|
||||
]
|
||||
])->send('Component Checked Out');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to the new component page
|
||||
return redirect()->to("admin/components")->with('success', trans('admin/components/message.checkout.success'));
|
||||
|
||||
|
||||
$component->assets()->attach($component->id, [
|
||||
'component_id' => $component->id,
|
||||
'user_id' => $admin_user->id,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'assigned_qty' => Input::get('assigned_qty'),
|
||||
'asset_id' => $asset_id
|
||||
]);
|
||||
|
||||
$component->logCheckout(e(Input::get('note')), $asset);
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON response for accessories listing view.
|
||||
*
|
||||
* For debugging, see at /api/accessories/list
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return string JSON
|
||||
**/
|
||||
public function getDatatable()
|
||||
* 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)
|
||||
{
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')->whereNull('components.deleted_at')
|
||||
->with('company', 'location', 'category'));
|
||||
|
||||
if (Input::has('search')) {
|
||||
$components = $components->TextSearch(Input::get('search'));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','companyName','category','total_qty'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$components = $components->OrderCategory($order);
|
||||
break;
|
||||
case 'location':
|
||||
$components = $components->OrderLocation($order);
|
||||
break;
|
||||
case 'companyName':
|
||||
$components = $components->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$components = $components->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$consumCount = $components->count();
|
||||
$components = $components->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($components as $component) {
|
||||
$actions = '<nobr>';
|
||||
if (Gate::allows('components.checkout')) {
|
||||
$actions .= '<a href="' . route('checkout/component',
|
||||
$component->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm ' . (($component->numRemaining() > 0) ? '' : ' disabled') . '" ' . (($component->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
|
||||
// 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 (Gate::allows('components.edit')) {
|
||||
$actions .= '<a href="' . route('update/component',
|
||||
$component->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
if (is_null($asset = Asset::find($component_assets->asset_id))) {
|
||||
return redirect()->route('components.index')->with('error',
|
||||
trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
if (Gate::allows('components.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/component',
|
||||
$component->id) . '" data-content="' . trans('admin/components/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($component->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
|
||||
$actions .='</nobr>';
|
||||
$company = $component->company;
|
||||
|
||||
$rows[] = array(
|
||||
'checkbox' =>'<div class="text-center"><input type="checkbox" name="component['.$component->id.']" class="one_required"></div>',
|
||||
'id' => $component->id,
|
||||
'name' => (string)link_to('admin/components/'.$component->id.'/view', e($component->name)),
|
||||
'serial_number' => $component->serial,
|
||||
'location' => ($component->location) ? e($component->location->name) : '',
|
||||
'qty' => e($component->qty),
|
||||
'min_amt' => e($component->min_amt),
|
||||
'category' => ($component->category) ? e($component->category->name) : 'Missing category',
|
||||
'order_number' => e($component->order_number),
|
||||
'purchase_date' => e($component->purchase_date),
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost),
|
||||
'numRemaining' => $component->numRemaining(),
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($company) ? '' : e($company->name),
|
||||
);
|
||||
$this->authorize('checkin', $component);
|
||||
return view('components/checkin', compact('component_assets','component','asset'));
|
||||
}
|
||||
|
||||
$data = array('total' => $consumCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
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)
|
||||
{
|
||||
//$component = Component::find($componentID);
|
||||
$component = Component::with('assets')->find($componentId);
|
||||
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('checkin', $component);
|
||||
|
||||
$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'));
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($component->assets as $component_assignment) {
|
||||
$rows[] = array(
|
||||
'name' => (string)link_to('/hardware/'.$component_assignment->id.'/view', e($component_assignment->showAssetName())),
|
||||
'qty' => e($component_assignment->pivot->assigned_qty),
|
||||
'created_at' => ($component_assignment->created_at->format('Y-m-d H:i:s')=='-0001-11-30 00:00:00') ? '' : $component_assignment->created_at->format('Y-m-d H:i:s'),
|
||||
);
|
||||
}
|
||||
|
||||
$componentCount = $component->assets->count();
|
||||
$data = array('total' => $componentCount, 'rows' => $rows);
|
||||
return $data;
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Setting;
|
||||
@@ -13,12 +12,13 @@ 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
|
||||
@@ -34,11 +34,12 @@ class ConsumablesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return View::make('consumables/index');
|
||||
$this->authorize('index', Consumable::class);
|
||||
return view('consumables/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -48,22 +49,14 @@ class ConsumablesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::postCreate() method that stores the form data
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// Show the page
|
||||
$category_list = Helper::categoryList('consumable');
|
||||
$company_list = Helper::companyList();
|
||||
$location_list = Helper::locationsList();
|
||||
$manufacturer_list = Helper::manufacturerList();
|
||||
|
||||
return View::make('consumables/edit')
|
||||
->with('item', new Consumable)
|
||||
->with('category_list', $category_list)
|
||||
->with('company_list', $company_list)
|
||||
->with('location_list', $location_list)
|
||||
->with('manufacturer_list', $manufacturer_list);
|
||||
$this->authorize('create', Consumable::class);
|
||||
$category_type = 'consumable';
|
||||
return view('consumables/edit')->with('category_type', $category_type)
|
||||
->with('item', new Consumable);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,46 +66,44 @@ class ConsumablesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::getCreate() method that returns the form view
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable();
|
||||
$consumable->name = e(Input::get('name'));
|
||||
$consumable->category_id = e(Input::get('category_id'));
|
||||
$consumable->location_id = e(Input::get('location_id'));
|
||||
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$consumable->order_number = e(Input::get('order_number'));
|
||||
$consumable->min_amt = e(Input::get('min_amt'));
|
||||
$consumable->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$consumable->model_number = e(Input::get('model_number'));
|
||||
$consumable->item_no = e(Input::get('item_no'));
|
||||
$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();
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$consumable->purchase_date = null;
|
||||
} else {
|
||||
$consumable->purchase_date = e(Input::get('purchase_date'));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$consumable->purchase_cost = null;
|
||||
} else {
|
||||
$consumable->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
|
||||
}
|
||||
|
||||
$consumable->qty = e(Input::get('qty'));
|
||||
$consumable->user_id = Auth::user()->id;
|
||||
|
||||
// Was the consumable created?
|
||||
if ($consumable->save()) {
|
||||
$consumable->logCreate();
|
||||
// Redirect to the new consumable page
|
||||
return redirect()->to("admin/consumables")->with('success', trans('admin/consumables/message.create.success'));
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,28 +113,18 @@ class ConsumablesController extends Controller
|
||||
* @param int $consumableId
|
||||
* @see ConsumablesController::postEdit() method that stores the form data.
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($consumableId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($consumableId = null)
|
||||
{
|
||||
// Check if the consumable exists
|
||||
if (is_null($item = Consumable::find($consumableId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($item)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
if ($item = Consumable::find($consumableId)) {
|
||||
$this->authorize($item);
|
||||
$category_type = 'consumable';
|
||||
return view('consumables/edit', compact('item'))->with('category_type', $category_type);
|
||||
}
|
||||
|
||||
$category_list = Helper::categoryList('consumable');
|
||||
$company_list = Helper::companyList();
|
||||
$location_list = Helper::locationsList();
|
||||
$manufacturer_list = Helper::manufacturerList();
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
|
||||
return View::make('consumables/edit', compact('item'))
|
||||
->with('category_list', $category_list)
|
||||
->with('company_list', $company_list)
|
||||
->with('location_list', $location_list)
|
||||
->with('manufacturer_list', $manufacturer_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,46 +135,46 @@ class ConsumablesController extends Controller
|
||||
* @param int $consumableId
|
||||
* @see ConsumablesController::getEdit() method that stores the form data.
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($consumableId = null)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $consumableId = null)
|
||||
{
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$consumable->name = e(Input::get('name'));
|
||||
$consumable->category_id = e(Input::get('category_id'));
|
||||
$consumable->location_id = e(Input::get('location_id'));
|
||||
$consumable->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
|
||||
$consumable->order_number = e(Input::get('order_number'));
|
||||
$consumable->min_amt = e(Input::get('min_amt'));
|
||||
$consumable->manufacturer_id = e(Input::get('manufacturer_id'));
|
||||
$consumable->model_number = e(Input::get('model_number'));
|
||||
$consumable->item_no = e(Input::get('item_no'));
|
||||
$this->authorize($consumable);
|
||||
|
||||
if (e(Input::get('purchase_date')) == '') {
|
||||
$consumable->purchase_date = null;
|
||||
} else {
|
||||
$consumable->purchase_date = e(Input::get('purchase_date'));
|
||||
$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 (e(Input::get('purchase_cost')) == '0.00') {
|
||||
$consumable->purchase_cost = null;
|
||||
} else {
|
||||
$consumable->purchase_cost = Helper::ParseFloat(e(Input::get('purchase_cost')));
|
||||
}
|
||||
|
||||
$consumable->qty = Helper::ParseFloat(e(Input::get('qty')));
|
||||
|
||||
if ($consumable->save()) {
|
||||
return redirect()->to("admin/consumables")->with('success', trans('admin/consumables/message.update.success'));
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,27 +183,19 @@ class ConsumablesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $consumableId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($consumableId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($consumableId)
|
||||
{
|
||||
// Check if the blog post exists
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/consumables')->with('error', trans('admin/consumables/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
|
||||
}
|
||||
|
||||
$consumable->delete();
|
||||
|
||||
// Redirect to the locations management page
|
||||
return redirect()->to('admin/consumables')->with('success', trans('admin/consumables/message.delete.success'));
|
||||
|
||||
$this->authorize($consumable);
|
||||
$consumable->delete();
|
||||
// Redirect to the locations management page
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a view to display component information.
|
||||
*
|
||||
@@ -230,29 +203,16 @@ class ConsumablesController extends Controller
|
||||
* @see ConsumablesController::getDataView() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return View
|
||||
*/
|
||||
public function getView($consumableId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($consumableId = null)
|
||||
{
|
||||
$consumable = Consumable::find($consumableId);
|
||||
|
||||
$this->authorize($consumable);
|
||||
if (isset($consumable->id)) {
|
||||
|
||||
|
||||
if (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
} else {
|
||||
return View::make('consumables/view', compact('consumable'));
|
||||
}
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/consumables/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('consumables')->with('error', $error);
|
||||
return view('consumables/view', compact('consumable'));
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist', compact('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,23 +222,15 @@ class ConsumablesController extends Controller
|
||||
* @see ConsumablesController::postCheckout() method that stores the data.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return View
|
||||
*/
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getCheckout($consumableId)
|
||||
{
|
||||
// Check if the consumable exists
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
// Redirect to the consumable management page with error
|
||||
return redirect()->to('consumables')->with('error', trans('admin/consumables/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
$users_list = Helper::usersList();
|
||||
|
||||
return View::make('consumables/checkout', compact('consumable'))->with('users_list', $users_list);
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
return view('consumables/checkout', compact('consumable'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,71 +240,35 @@ class ConsumablesController extends Controller
|
||||
* @see ConsumablesController::getCheckout() method that returns the form.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return Redirect
|
||||
*/
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postCheckout($consumableId)
|
||||
{
|
||||
// Check if the consumable exists
|
||||
if (is_null($consumable = Consumable::find($consumableId))) {
|
||||
// Redirect to the consumable management page with error
|
||||
return redirect()->to('consumables')->with('error', trans('admin/consumables/message.not_found'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($consumable)) {
|
||||
return redirect()->to('admin/consumables')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
|
||||
$admin_user = Auth::user();
|
||||
$assigned_to = e(Input::get('assigned_to'));
|
||||
|
||||
// Check if the user exists
|
||||
// Check if the user exists
|
||||
if (is_null($user = User::find($assigned_to))) {
|
||||
// Redirect to the consumable management page with error
|
||||
return redirect()->to('admin/consumables')->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
|
||||
// Update the consumable data
|
||||
$consumable->assigned_to = e(Input::get('assigned_to'));
|
||||
|
||||
$consumable->users()->attach($consumable->id, array(
|
||||
'consumable_id' => $consumable->id,
|
||||
'user_id' => $admin_user->id,
|
||||
'assigned_to' => e(Input::get('assigned_to'))));
|
||||
|
||||
$logaction = $consumable->logCheckout(e(Input::get('note')));
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'Checked Out:',
|
||||
'value' => 'Consumable <'.config('app.url').'/admin/consumables/'.$consumable->id.'/view'.'|'.$consumable->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
|
||||
],
|
||||
[
|
||||
'title' => 'Note:',
|
||||
'value' => e($logaction->note)
|
||||
],
|
||||
]
|
||||
])->send('Consumable Checked Out');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$consumable_user = DB::table('consumables_users')->where('assigned_to', '=', $consumable->assigned_to)->where('consumable_id', '=', $consumable->id)->first();
|
||||
$consumable->users()->attach($consumable->id, [
|
||||
'consumable_id' => $consumable->id,
|
||||
'user_id' => $admin_user->id,
|
||||
'assigned_to' => e(Input::get('assigned_to'))
|
||||
]);
|
||||
|
||||
$logaction = $consumable->logCheckout(e(Input::get('note')), $user);
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['eula'] = $consumable->getEula();
|
||||
$data['first_name'] = $user->first_name;
|
||||
@@ -362,168 +278,9 @@ class ConsumablesController extends Controller
|
||||
$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()->to("admin/consumables")->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
|
||||
|
||||
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the JSON response containing the the consumables data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumablesController::getIndex() method that returns the view that consumes the JSON.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @return View
|
||||
*/
|
||||
public function getDatatable()
|
||||
{
|
||||
$consumables = Company::scopeCompanyables(
|
||||
Consumable::select('consumables.*')
|
||||
->whereNull('consumables.deleted_at')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
||||
);
|
||||
|
||||
if (Input::has('search')) {
|
||||
$consumables = $consumables->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','companyName','category','model_number', 'item_no', 'manufacturer'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
case 'category':
|
||||
$consumables = $consumables->OrderCategory($order);
|
||||
break;
|
||||
case 'location':
|
||||
$consumables = $consumables->OrderLocation($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$consumables = $consumables->OrderManufacturer($order);
|
||||
break;
|
||||
case 'companyName':
|
||||
$consumables = $consumables->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$consumables = $consumables->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$consumCount = $consumables->count();
|
||||
$consumables = $consumables->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumables as $consumable) {
|
||||
$actions = '<nobr>';
|
||||
if (Gate::allows('consumables.checkout')) {
|
||||
$actions .= '<a href="' . route('checkout/consumable',
|
||||
$consumable->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($consumable->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
|
||||
}
|
||||
|
||||
if (Gate::allows('consumables.edit')) {
|
||||
$actions .= '<a href="' . route('update/consumable',
|
||||
$consumable->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
}
|
||||
if (Gate::allows('consumables.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/consumable',
|
||||
$consumable->id) . '" data-content="' . trans('admin/consumables/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($consumable->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
|
||||
$actions .='</nobr>';
|
||||
|
||||
$company = $consumable->company;
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $consumable->id,
|
||||
'name' => (string)link_to('admin/consumables/'.$consumable->id.'/view', e($consumable->name)),
|
||||
'location' => ($consumable->location) ? e($consumable->location->name) : '',
|
||||
'min_amt' => e($consumable->min_amt),
|
||||
'qty' => e($consumable->qty),
|
||||
'manufacturer' => ($consumable->manufacturer) ? (string) link_to('/admin/settings/manufacturers/'.$consumable->manufacturer_id.'/view', $consumable->manufacturer->name): '',
|
||||
'model_number' => e($consumable->model_number),
|
||||
'item_no' => e($consumable->item_no),
|
||||
'category' => ($consumable->category) ? (string) link_to('/admin/settings/categories/'.$consumable->category_id.'/view', $consumable->category->name) : 'Missing category',
|
||||
'order_number' => e($consumable->order_number),
|
||||
'purchase_date' => e($consumable->purchase_date),
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
|
||||
'numRemaining' => $consumable->numRemaining(),
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($company) ? '' : e($company->name),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $consumCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 View
|
||||
*/
|
||||
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' => []];
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumable->consumableAssigments as $consumable_assignment) {
|
||||
$rows[] = array(
|
||||
'name' => (string)link_to('/admin/users/'.$consumable_assignment->user->id.'/view', e($consumable_assignment->user->fullName())),
|
||||
'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) ? e($consumable_assignment->admin->fullName()) : '',
|
||||
);
|
||||
}
|
||||
|
||||
$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;
|
||||
@@ -11,6 +12,7 @@ use App\Models\AssetModel;
|
||||
use Lang;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\Helper;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -35,77 +37,16 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$fieldsets=CustomFieldset::with("fields", "models")->get();
|
||||
//$fieldsets=CustomFieldset::all();
|
||||
$fields=CustomField::with("fieldset")->get();
|
||||
//$fields=CustomField::all();
|
||||
return View::make("custom_fields.index")->with("custom_fieldsets", $fieldsets)->with("custom_fields", $fields);
|
||||
$this->authorize('view', CustomField::class);
|
||||
|
||||
$fieldsets = CustomFieldset::with("fields", "models")->get();
|
||||
$fields = CustomField::with("fieldset")->get();
|
||||
return view("custom_fields.index")->with("custom_fieldsets", $fieldsets)->with("custom_fields", $fields);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view with a form for creating a new custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
return View::make("custom_fields.create");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores a new custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
$cfset = new CustomFieldset(
|
||||
[
|
||||
"name" => e($request->get("name")),
|
||||
"user_id" => Auth::user()->id]
|
||||
);
|
||||
|
||||
$validator=Validator::make(Input::all(), $cfset->rules);
|
||||
if ($validator->passes()) {
|
||||
$cfset->save();
|
||||
return redirect()->route("admin.custom_fields.show", [$cfset->id])->with('success', trans('admin/custom_fields/message.fieldset.create.success'));
|
||||
} else {
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the custom field with a custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function associate($id)
|
||||
{
|
||||
|
||||
$set = CustomFieldset::find($id);
|
||||
|
||||
foreach ($set->fields as $field) {
|
||||
if ($field->id == Input::get('field_id')) {
|
||||
return redirect()->route("admin.custom_fields.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
|
||||
}
|
||||
}
|
||||
|
||||
$results=$set->fields()->attach(Input::get('field_id'), ["required" => (Input::get('required') == "on"),"order" => Input::get('order')]);
|
||||
|
||||
return redirect()->route("admin.custom_fields.show", [$id])->with("success", trans('admin/custom_fields/message.field.create.assoc_success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view with a form to create a new custom field.
|
||||
@@ -115,9 +56,11 @@ class CustomFieldsController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function createField()
|
||||
public function create()
|
||||
{
|
||||
return View::make("custom_fields.create_field");
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', new CustomField());
|
||||
}
|
||||
|
||||
|
||||
@@ -129,38 +72,33 @@ class CustomFieldsController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function storeField(Request $request)
|
||||
public function store(CustomFieldRequest $request)
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$field = new CustomField([
|
||||
"name" => e($request->get("name")),
|
||||
"element" => e($request->get("element")),
|
||||
"field_values" => e($request->get("field_values")),
|
||||
"field_encrypted" => e($request->get("field_encrypted", 0)),
|
||||
"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("admin.custom_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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +113,10 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
if ($field->fieldset()->detach($fieldset_id)) {
|
||||
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
|
||||
@@ -189,123 +129,76 @@ class CustomFieldsController extends Controller
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function deleteField($field_id)
|
||||
public function destroy($field_id)
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->fieldset->count()>0) {
|
||||
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
|
||||
} else {
|
||||
$field->delete();
|
||||
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores a new custom field.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')->where('id','=',$id)->orderBy('id','ASC')->first();
|
||||
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::lists("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::make("custom_fields.show")->with("custom_fieldset", $cfset)->with("maxid", $maxid+1)->with("custom_fields_list", $custom_fields_list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* What the actual fuck, Brady?
|
||||
* Return a view to edit a custom field
|
||||
*
|
||||
* @todo Uhh, build this?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', $field);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET IN THE SEA BRADY.
|
||||
* Store the updated field
|
||||
*
|
||||
* @todo Uhh, build this too?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
*/
|
||||
public function update($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates a custom fieldset and then deletes if it has no models associated.
|
||||
* @todo Allow encrypting/decrypting if encryption status changes
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
* @since [v4.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function destroy($id)
|
||||
public function update(CustomFieldRequest $request, $id)
|
||||
{
|
||||
//
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$models = AssetModel::where("fieldset_id", "=", $id);
|
||||
if ($models->count() == 0) {
|
||||
$fieldset->delete();
|
||||
return redirect()->route("admin.custom_fields.index")->with("success", trans('admin/custom_fields/message.fieldset.delete.success'));
|
||||
$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->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"));
|
||||
} else {
|
||||
return redirect()->route("admin.custom_fields.index")->with("error", trans('admin/custom_fields/message.fieldset.delete.in_use'));
|
||||
$field->format = e($request->get("format"));
|
||||
}
|
||||
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reorder the custom fields within a fieldset
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v3.0]
|
||||
* @return Array
|
||||
*/
|
||||
public function postReorder(Request $request, $id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
$fields = array();
|
||||
$order_array = array();
|
||||
|
||||
$items = $request->input('item');
|
||||
|
||||
foreach ($items as $order => $field_id) {
|
||||
$order_array[$field_id] = $order;
|
||||
}
|
||||
|
||||
foreach ($fieldset->fields as $field) {
|
||||
$fields[$field->id] = ['required' => $field->pivot->required, 'order' => $order_array[$field->id]];
|
||||
}
|
||||
|
||||
return $fieldset->fields()->sync($fields);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
192
app/Http/Controllers/CustomFieldsetsController.php
Normal file
192
app/Http/Controllers/CustomFieldsetsController.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use View;
|
||||
use App\Models\CustomFieldset;
|
||||
use App\Models\CustomField;
|
||||
use Input;
|
||||
use Validator;
|
||||
use Redirect;
|
||||
use App\Models\AssetModel;
|
||||
use Lang;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Custom Asset Fields for
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @todo Improve documentation here.
|
||||
* @todo Check for raw DB queries and try to convert them to query builder statements
|
||||
* @version v2.0
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
*/
|
||||
|
||||
class CustomFieldsetsController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Validates and stores a new custom field.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
|
||||
|
||||
$this->authorize('view', $cfset);
|
||||
|
||||
if ($cfset) {
|
||||
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
|
||||
|
||||
$maxid = 0;
|
||||
foreach ($cfset->fields as $field) {
|
||||
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 redirect()->route("fields.index")->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view with a form for creating a new custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
return view("custom_fields.fieldsets.edit");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores a new custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
$cfset = new CustomFieldset(
|
||||
[
|
||||
"name" => e($request->get("name")),
|
||||
"user_id" => Auth::user()->id]
|
||||
);
|
||||
|
||||
$validator = Validator::make(Input::all(), $cfset->rules);
|
||||
if ($validator->passes()) {
|
||||
$cfset->save();
|
||||
return redirect()->route("fieldsets.show", [$cfset->id])->with('success', trans('admin/custom_fields/message.fieldset.create.success'));
|
||||
} else {
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* What the actual fuck, Brady?
|
||||
*
|
||||
* @todo Uhh, build this?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET IN THE SEA BRADY.
|
||||
*
|
||||
* @todo Uhh, build this too?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
*/
|
||||
public function update($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates a custom fieldset and then deletes if it has no models associated.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$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'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Associate the custom field with a custom fieldset.
|
||||
*
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function associate(Request $request, $id)
|
||||
{
|
||||
|
||||
$set = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('update', $set);
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
@@ -30,53 +30,20 @@ class DashboardController extends Controller
|
||||
// Show the page
|
||||
if (Auth::user()->hasAccess('admin')) {
|
||||
|
||||
$asset_stats['total'] = Asset::Hardware()->count();
|
||||
$asset_stats=null;
|
||||
|
||||
$asset_stats['rtd']['total'] = Asset::Hardware()->RTD()->count();
|
||||
$counts['asset'] = \App\Models\Asset::count();
|
||||
$counts['accessory'] = \App\Models\Accessory::count();
|
||||
$counts['license'] = \App\Models\License::assetcount();
|
||||
$counts['consumable'] = \App\Models\Consumable::count();
|
||||
$counts['grand_total'] = $counts['asset'] + $counts['accessory'] + $counts['license'] + $counts['consumable'];
|
||||
|
||||
if ($asset_stats['rtd']['total'] > 0) {
|
||||
$asset_stats['rtd']['percent'] = round(($asset_stats['rtd']['total']/$asset_stats['total']) * 100);
|
||||
} else {
|
||||
$asset_stats['rtd']['percent'] = 0;
|
||||
if ((!file_exists(storage_path().'/oauth-private.key')) || (!file_exists(storage_path().'/oauth-public.key'))) {
|
||||
\Artisan::call('migrate', ['--force' => true]);
|
||||
\Artisan::call('passport:install');
|
||||
}
|
||||
|
||||
|
||||
$asset_stats['pending']['total'] = Asset::Hardware()->Pending()->count();
|
||||
|
||||
if ($asset_stats['pending']['total'] > 0) {
|
||||
$asset_stats['pending']['percent'] = round(($asset_stats['pending']['total']/$asset_stats['total']) * 100);
|
||||
} else {
|
||||
$asset_stats['pending']['percent'] = 0;
|
||||
}
|
||||
|
||||
|
||||
$asset_stats['deployed']['total'] = Asset::Hardware()->Deployed()->count();
|
||||
|
||||
if ($asset_stats['deployed']['total'] > 0) {
|
||||
$asset_stats['deployed']['percent'] = round(($asset_stats['deployed']['total']/$asset_stats['total']) * 100);
|
||||
} else {
|
||||
$asset_stats['deployed']['percent'] = 0;
|
||||
}
|
||||
|
||||
|
||||
$asset_stats['undeployable']['total'] = Asset::Hardware()->Undeployable()->count();
|
||||
|
||||
if ($asset_stats['undeployable']['total'] > 0) {
|
||||
$asset_stats['undeployable']['percent'] = round(($asset_stats['undeployable']['total']/$asset_stats['total']) * 100);
|
||||
} else {
|
||||
$asset_stats['undeployable']['percent'] = 0;
|
||||
}
|
||||
|
||||
$asset_stats['archived']['total'] = Asset::Hardware()->Archived()->count();
|
||||
|
||||
if ($asset_stats['archived']['total'] > 0) {
|
||||
$asset_stats['archived']['percent'] = round(($asset_stats['archived']['total']/$asset_stats['total']) * 100);
|
||||
} else {
|
||||
$asset_stats['archived']['percent'] = 0;
|
||||
}
|
||||
|
||||
|
||||
return View::make('dashboard')->with('asset_stats', $asset_stats);
|
||||
return view('dashboard')->with('asset_stats', $asset_stats)->with('counts', $counts);
|
||||
} else {
|
||||
// Redirect to the profile page
|
||||
return redirect()->intended('account/view-assets');
|
||||
|
||||
203
app/Http/Controllers/DepartmentsController.php
Normal file
203
app/Http/Controllers/DepartmentsController.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
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
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the assets listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetController::getDatatable() method that generates the JSON response
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('index', Department::class);
|
||||
$company = null;
|
||||
if ($request->filled('company_id')) {
|
||||
$company = Company::find($request->input('company_id'));
|
||||
}
|
||||
return view('departments/index')->with('company', $company);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department->user_id = Auth::user()->id;
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/departments/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$department->image = $file_name;
|
||||
}
|
||||
|
||||
if ($department->save()) {
|
||||
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.create.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($department->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the department detail page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$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')));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a form view used to create a new department.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see DepartmentsController::postCreate() method that validates and stores the data
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
|
||||
return view('departments/edit')->with('item', new Department);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and deletes selected department.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $locationId
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
if (is_null($department = Department::find($id))) {
|
||||
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'));
|
||||
}
|
||||
|
||||
$department->delete();
|
||||
return redirect()->back()->with('success', trans('admin/departments/message.delete.success'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a form view to edit location information.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::postCreate() method that validates and stores
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
if (is_null($item = Department::find($id))) {
|
||||
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('departments/edit', compact('item'));
|
||||
}
|
||||
|
||||
public function update(ImageUploadRequest $request, $id) {
|
||||
|
||||
if (is_null($department = Department::find($id))) {
|
||||
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $department);
|
||||
|
||||
$department->fill($request->all());
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
$old_image = $department->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$department->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $department->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('departments_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('departments_upload_path'), $file_name);
|
||||
}
|
||||
$department->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('departments_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
if ($department->save()) {
|
||||
return redirect()->route("departments.index")->with('success', trans('admin/departments/message.update.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($department->getErrors());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Input;
|
||||
use App\Helpers\Helper;
|
||||
use Lang;
|
||||
use App\Models\Depreciation;
|
||||
use Redirect;
|
||||
@@ -10,6 +10,7 @@ use DB;
|
||||
use Str;
|
||||
use View;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Depreciations for
|
||||
@@ -26,12 +27,14 @@ class DepreciationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return View::make('depreciations/index', compact('depreciations'));
|
||||
return view('depreciations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -41,45 +44,43 @@ class DepreciationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::postCreate()
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return View::make('depreciations/edit')->with('item', new Depreciation);
|
||||
return view('depreciations/edit')->with('item', new Depreciation);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the new depreciation data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::postCreate()
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* Validates and stores the new depreciation data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::postCreate()
|
||||
* @since [v1.0]
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// get the POST data
|
||||
$new = Input::all();
|
||||
|
||||
// create a new instance
|
||||
// create a new instance
|
||||
$depreciation = new Depreciation();
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
$depreciation->user_id = Auth::id();
|
||||
|
||||
// Depreciation data
|
||||
$depreciation->name = e(Input::get('name'));
|
||||
$depreciation->months = e(Input::get('months'));
|
||||
$depreciation->user_id = Auth::user()->id;
|
||||
|
||||
// Was the asset created?
|
||||
// Was the asset created?
|
||||
if ($depreciation->save()) {
|
||||
// Redirect to the new depreciation page
|
||||
return redirect()->to("admin/settings/depreciations")->with('success', trans('admin/depreciations/message.create.success'));
|
||||
return redirect()->route('depreciations.index')->with('success', trans('admin/depreciations/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($depreciation->getErrors());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,138 +90,103 @@ class DepreciationsController extends Controller
|
||||
* @see DepreciationsController::postEdit()
|
||||
* @param int $depreciationId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($depreciationId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($depreciationId = null)
|
||||
{
|
||||
// Check if the depreciation exists
|
||||
if (is_null($item = Depreciation::find($depreciationId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/settings/depreciations')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
return View::make('depreciations/edit', compact('item'));
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('depreciations/edit', compact('item'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the updated depreciation data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::getEdit()
|
||||
* @param int $depreciationId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($depreciationId = null)
|
||||
* Validates and stores the updated depreciation data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see DepreciationsController::getEdit()
|
||||
* @param Request $request
|
||||
* @param int $depreciationId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function update(Request $request, $depreciationId = null)
|
||||
{
|
||||
// Check if the depreciation exists
|
||||
if (is_null($depreciation = Depreciation::find($depreciationId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/settings/depreciations')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $depreciation);
|
||||
|
||||
// Depreciation data
|
||||
$depreciation->name = e(Input::get('name'));
|
||||
$depreciation->months = e(Input::get('months'));
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
|
||||
// Was the asset created?
|
||||
if ($depreciation->save()) {
|
||||
// Redirect to the depreciation page
|
||||
return redirect()->to("admin/settings/depreciations/")->with('success', trans('admin/depreciations/message.update.success'));
|
||||
return redirect()->route("depreciations.index")->with('success', trans('admin/depreciations/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($depreciation->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and deletes a selected depreciation.
|
||||
*
|
||||
* This is a hard-delete. We do not currently soft-delete depreciations.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($depreciationId)
|
||||
* Validates and deletes a selected depreciation.
|
||||
*
|
||||
* This is a hard-delete. We do not currently soft-delete depreciations.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @since [v1.0]
|
||||
* @param integer $depreciationId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($depreciationId)
|
||||
{
|
||||
// Check if the depreciation exists
|
||||
if (is_null($depreciation = Depreciation::find($depreciationId))) {
|
||||
// Redirect to the blogs management page
|
||||
return redirect()->to('admin/settings/depreciations')->with('error', trans('admin/depreciations/message.not_found'));
|
||||
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()->to('admin/settings/depreciations')->with('error', trans('admin/depreciations/message.assoc_users'));
|
||||
} else {
|
||||
|
||||
$depreciation->delete();
|
||||
|
||||
// Redirect to the depreciations management page
|
||||
return redirect()->to('admin/settings/depreciations')->with('success', trans('admin/depreciations/message.delete.success'));
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.assoc_users'));
|
||||
}
|
||||
|
||||
$depreciation->delete();
|
||||
// Redirect to the depreciations management page
|
||||
return redirect()->route('depreciations.index')->with('success', trans('admin/depreciations/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON used to display the depreciation listing.
|
||||
*
|
||||
* @see DepreciationsController::getIndex()
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param string $status
|
||||
* @since [v1.2]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable()
|
||||
* 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)
|
||||
{
|
||||
$depreciations = Depreciation::select(array('id','name','months'));
|
||||
|
||||
if (Input::has('search')) {
|
||||
$depreciations = $depreciations->TextSearch(e(Input::get('search')));
|
||||
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'));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','name','months'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
|
||||
$depreciations->orderBy($sort, $order);
|
||||
|
||||
$depreciationsCount = $depreciations->count();
|
||||
$depreciations = $depreciations->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($depreciations as $depreciation) {
|
||||
$actions = '<a href="'.route('update/depreciations', $depreciation->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/depreciations', $depreciation->id).'" data-content="'.trans('admin/depreciations/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($depreciation->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $depreciation->id,
|
||||
'name' => e($depreciation->name),
|
||||
'months' => e($depreciation->months),
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $depreciationsCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
$this->authorize('view', $depreciation);
|
||||
|
||||
return view('depreciations/view', compact('depreciation'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ class GroupsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see GroupsController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return View::make('groups/index', compact('groups'));
|
||||
return view('groups/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,18 +40,18 @@ class GroupsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see GroupsController::postCreate()
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$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
|
||||
return View::make('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))->with('group', $group);
|
||||
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))->with('group', $group);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,9 +60,9 @@ class GroupsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @see GroupsController::getCreate()
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
// create a new group instance
|
||||
$group = new Group();
|
||||
@@ -70,12 +70,9 @@ class GroupsController extends Controller
|
||||
$group->permissions = json_encode(Input::get('permission'));
|
||||
|
||||
if ($group->save()) {
|
||||
return redirect()->to("admin/groups")->with('success', trans('admin/groups/message.success.create'));
|
||||
return redirect()->route("groups.index")->with('success', trans('admin/groups/message.success.create'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($group->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,15 +82,20 @@ class GroupsController extends Controller
|
||||
* @see GroupsController::postEdit()
|
||||
* @param int $id
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($id = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$group = Group::find($id);
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = $group->decodePermissions();
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
return View::make('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'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,30 +105,24 @@ class GroupsController extends Controller
|
||||
* @see GroupsController::getEdit()
|
||||
* @param int $id
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($id = null)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update($id = null)
|
||||
{
|
||||
$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'));
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
if ($group->save()) {
|
||||
return redirect()->to("admin/groups")->with('success', trans('admin/groups/message.success.update'));
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.update'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($group->getErrors());
|
||||
|
||||
} else {
|
||||
return redirect()->route('update/group', $id)->withInput()->with('error', 'Denied! Editing groups is not allowed in the demo.');
|
||||
}
|
||||
|
||||
return redirect()->route('groups.index')->with('error', trans('general.feature_disabled'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,103 +132,39 @@ class GroupsController extends Controller
|
||||
* @see GroupsController::getEdit()
|
||||
* @param int $id
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($id = null)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy($id = null)
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
try {
|
||||
// Get group information
|
||||
$group = Group::find($id);
|
||||
$group->delete();
|
||||
|
||||
// Redirect to the group management page
|
||||
return redirect()->route('groups')->with('success', trans('admin/groups/message.success.delete'));
|
||||
} catch (GroupNotFoundException $e) {
|
||||
// Redirect to the group management page
|
||||
return redirect()->route('groups')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
if (!$group = Group::find($id)) {
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
} else {
|
||||
return redirect()->route('groups')->with('error', trans('general.feature_disabled'));
|
||||
$group->delete();
|
||||
// Redirect to the group management page
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.delete'));
|
||||
}
|
||||
return redirect()->route('groups.index')->with('error', trans('general.feature_disabled'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the JSON used to display the User Group listing.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable()
|
||||
* 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 (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
if ($group) {
|
||||
return view('groups/view', compact('group'));
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
if (Input::get('sort')=='name') {
|
||||
$sort = 'first_name';
|
||||
} else {
|
||||
$sort = e(Input::get('sort'));
|
||||
}
|
||||
|
||||
// Grab all the groups
|
||||
$groups = Group::with('users')->orderBy('name', 'ASC');
|
||||
//$users = Company::scopeCompanyables($users);
|
||||
|
||||
if (Input::has('search')) {
|
||||
$groups = $users->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns =
|
||||
[
|
||||
'name','created_at'
|
||||
];
|
||||
|
||||
$sort = in_array($sort, $allowed_columns) ? $sort : 'name';
|
||||
$groups = $groups->orderBy($sort, $order);
|
||||
|
||||
$groupsCount = $groups->count();
|
||||
$groups = $groups->skip($offset)->take($limit)->get();
|
||||
$rows = array();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$group_names = '';
|
||||
$inout = '';
|
||||
$actions = '<nobr>';
|
||||
|
||||
$actions .= '<a href="' . route('update/group', $group->id) . '" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> ';
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/group', $group->id) . '" data-content="'.trans('admin/groups/message.delete.confirm').'" data-title="Delete ' . htmlspecialchars($group->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a> ';
|
||||
} else {
|
||||
$actions .= ' <span class="btn delete-asset btn-danger btn-sm disabled"><i class="fa fa-trash icon-white"></i></span>';
|
||||
}
|
||||
|
||||
$actions .= '</nobr>';
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
'users' => $group->users->count(),
|
||||
'created_at' => $group->created_at->format('Y-m-d'),
|
||||
'actions' => ($actions) ? $actions : '',
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total'=>$groupsCount, 'rows'=>$rows);
|
||||
return $data;
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
20
app/Http/Controllers/ImportsController.php
Normal file
20
app/Http/Controllers/ImportsController.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
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('import');
|
||||
$imports = Import::latest()->get();
|
||||
$imports = (new ImportsTransformer)->transformImports($imports);
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use Input;
|
||||
use Lang;
|
||||
use App\Models\Location;
|
||||
use phpDocumentor\Reflection\Types\Array_;
|
||||
use Redirect;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
@@ -14,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
|
||||
@@ -31,15 +35,14 @@ class LocationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
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::make('locations/index', compact('locations'));
|
||||
return view('locations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -49,19 +52,13 @@ class LocationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::postCreate() method that validates and stores the data
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
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;
|
||||
|
||||
return View::make('locations/edit')
|
||||
->with('location_options', $location_options)
|
||||
->with('item', new Location);
|
||||
$this->authorize('create', Location::class);
|
||||
return view('locations/edit')
|
||||
->with('item', new Location);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,78 +69,40 @@ class LocationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getCreate() method that makes the form
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
// create a new location instance
|
||||
$this->authorize('create', Location::class);
|
||||
$location = new Location();
|
||||
$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();
|
||||
|
||||
|
||||
// Save the location data
|
||||
$location->name = e(Input::get('name'));
|
||||
if (Input::get('parent_id')=='') {
|
||||
$location->parent_id = null;
|
||||
} else {
|
||||
$location->parent_id = e(Input::get('parent_id'));
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_random(25).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/locations/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$location->image = $file_name;
|
||||
}
|
||||
$location->currency = e(Input::get('currency', '$'));
|
||||
$location->address = e(Input::get('address'));
|
||||
$location->address2 = e(Input::get('address2'));
|
||||
$location->city = e(Input::get('city'));
|
||||
$location->state = e(Input::get('state'));
|
||||
$location->country = e(Input::get('country'));
|
||||
$location->zip = e(Input::get('zip'));
|
||||
$location->user_id = Auth::user()->id;
|
||||
|
||||
if ($location->save()) {
|
||||
// Redirect to the new location page
|
||||
return redirect()->to("admin/settings/locations")->with('success', trans('admin/locations/message.create.success'));
|
||||
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 store()
|
||||
{
|
||||
|
||||
$new['currency']=Setting::first()->default_currency;
|
||||
|
||||
// create a new location instance
|
||||
$location = new Location();
|
||||
|
||||
// Save the location data
|
||||
$location->name = e(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 = e(Input::get('city'));
|
||||
$location->state = '';//e(Input::get('state'));
|
||||
$location->country = e(Input::get('country'));
|
||||
// $location->zip = e(Input::get('zip'));
|
||||
$location->user_id = Auth::user()->id;
|
||||
|
||||
// Was the location created?
|
||||
if ($location->save()) {
|
||||
return JsonResponse::create($location);
|
||||
|
||||
}
|
||||
|
||||
// failure
|
||||
$errors = $location->errors();
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($location->getErrors(), true)], 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -154,22 +113,18 @@ class LocationsController extends Controller
|
||||
* @see LocationsController::postCreate() method that validates and stores
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($locationId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
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::make('locations/edit', compact('item'))->with('location_options', $location_options);
|
||||
return view('locations/edit', compact('item'));
|
||||
}
|
||||
|
||||
|
||||
@@ -180,40 +135,70 @@ class LocationsController extends Controller
|
||||
* @see LocationsController::getEdit() method that makes the form view
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postEdit($locationId = null)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $locationId = null)
|
||||
{
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
// Redirect to the blogs management page
|
||||
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 = e(Input::get('name'));
|
||||
if (Input::get('parent_id')=='') {
|
||||
$location->parent_id = null;
|
||||
} else {
|
||||
$location->parent_id = e(Input::get('parent_id', ''));
|
||||
}
|
||||
$location->currency = e(Input::get('currency', '$'));
|
||||
$location->address = e(Input::get('address'));
|
||||
$location->address2 = e(Input::get('address2'));
|
||||
$location->city = e(Input::get('city'));
|
||||
$location->state = e(Input::get('state'));
|
||||
$location->country = e(Input::get('country'));
|
||||
$location->zip = e(Input::get('zip'));
|
||||
$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');
|
||||
|
||||
$old_image = $location->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$location->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $location->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('locations_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('locations_upload_path'), $file_name);
|
||||
}
|
||||
$location->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('locations_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Was the asset created?
|
||||
if ($location->save()) {
|
||||
// Redirect to the saved location page
|
||||
return redirect()->to("admin/settings/locations/")->with('success', trans('admin/locations/message.update.success'));
|
||||
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());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,32 +207,31 @@ class LocationsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getDelete($locationId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
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('admin/settings/locations')->with('error', trans('admin/locations/message.not_found'));
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found'));
|
||||
}
|
||||
|
||||
|
||||
if ($location->users->count() > 0) {
|
||||
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_users'));
|
||||
} elseif ($location->childLocations->count() > 0) {
|
||||
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_child_loc'));
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_users'));
|
||||
|
||||
} 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('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
|
||||
} elseif ($location->assignedassets->count() > 0) {
|
||||
return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets'));
|
||||
|
||||
} else {
|
||||
$location->delete();
|
||||
return redirect()->to('admin/settings/locations')->with('success', trans('admin/locations/message.delete.success'));
|
||||
return redirect()->to(route('locations.index'))->with('success', trans('admin/locations/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -256,178 +240,19 @@ class LocationsController extends Controller
|
||||
* the content for the locations detail page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getDataViewUsers() method that returns JSON for location users
|
||||
* @see LocationsController::getDataViewAssets() method that returns JSON for location assets
|
||||
* @param int $locationId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getView($locationId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show($locationId = null)
|
||||
{
|
||||
$location = Location::find($locationId);
|
||||
|
||||
if (isset($location->id)) {
|
||||
return View::make('locations/view', compact('location'));
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/locations/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('locations')->with('error', $error);
|
||||
return view('locations/view', compact('location'));
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist', compact('id')));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the JSON response to populate the bootstrap tables on the locationa view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getIndex() method that returns JSON for location index
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getDatatable()
|
||||
{
|
||||
$locations = Location::select(array('locations.id','locations.name','locations.address','locations.address2','locations.city','locations.state','locations.zip','locations.country','locations.parent_id','locations.currency'))->with('assets');
|
||||
|
||||
|
||||
if (Input::has('search')) {
|
||||
$locations = $locations->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
|
||||
switch (Input::get('sort')) {
|
||||
case 'parent':
|
||||
$locations = $locations->OrderParent($order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns = ['id','name','address','city','state','country','currency','zip'];
|
||||
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$locations = $locations->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$locationsCount = $locations->count();
|
||||
$locations = $locations->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($locations as $location) {
|
||||
$actions = '<nobr><a href="'.route('update/location', $location->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/location', $location->id).'" data-content="'.trans('admin/locations/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($location->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></nobr>';
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $location->id,
|
||||
'name' => (string)link_to('admin/settings/locations/'.$location->id.'/view', e($location->name)),
|
||||
'parent' => ($location->parent) ? e($location->parent->name) : '',
|
||||
// 'assets' => ($location->assets->count() + $location->assignedassets->count()),
|
||||
'assets_default' => $location->assignedassets->count(),
|
||||
'assets_checkedout' => $location->assets->count(),
|
||||
'address' => ($location->address) ? e($location->address): '',
|
||||
'city' => e($location->city),
|
||||
'state' => e($location->state),
|
||||
'zip' => e($location->zip),
|
||||
'country' => e($location->country),
|
||||
'currency' => e($location->currency),
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $locationsCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON response that contains the users association with the
|
||||
* selected location, to be used by the location detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getView() method that creates the display view
|
||||
* @param int $locationId
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getDataViewUsers($locationID)
|
||||
{
|
||||
$location = Location::find($locationID);
|
||||
$users = User::where('location_id', '=', $location->id);
|
||||
|
||||
if (Input::has('search')) {
|
||||
$users = $users->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
$users = $users->get();
|
||||
$rows = array();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$rows[] = array(
|
||||
'name' => (string)link_to('/admin/users/'.$user->id.'/view', e($user->fullName()))
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $users->count(), 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON response that contains the assets association with the
|
||||
* selected location, to be used by the location detail view.
|
||||
*
|
||||
* @todo This is broken for accessories and consumables.
|
||||
* @todo This is a very naive implementation. Should clean this up with query scopes.
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see LocationsController::getView() method that creates the display view
|
||||
* @param int $locationID
|
||||
* @since [v1.8]
|
||||
* @return View
|
||||
*/
|
||||
public function getDataViewAssets($locationID)
|
||||
{
|
||||
$location = Location::find($locationID)->load('assignedassets.model');
|
||||
$assets = Asset::AssetsByLocation($location);
|
||||
|
||||
if (Input::has('search')) {
|
||||
$assets = $assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
$assets = $assets->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$rows[] = array(
|
||||
'name' => (string)link_to(config('app.url').'/hardware/'.$asset->id.'/view', e($asset->showAssetName())),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
'model' => e($asset->model->name),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $assets->count(), 'rows' => $rows);
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Setting;
|
||||
use Auth;
|
||||
use Exception;
|
||||
use Gate;
|
||||
use Input;
|
||||
use Lang;
|
||||
use Redirect;
|
||||
use Str;
|
||||
use View;
|
||||
use Illuminate\Http\Request;
|
||||
use Image;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Manufacturers for
|
||||
@@ -25,14 +29,14 @@ class ManufacturersController extends Controller
|
||||
* the content for the manufacturers listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getDatatable() method that generates the JSON response
|
||||
* @see Api\ManufacturersController::index() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getIndex()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return View::make('manufacturers/index', compact('manufacturers'));
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
return view('manufacturers/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -40,89 +44,141 @@ class ManufacturersController extends Controller
|
||||
* Returns a view that displays a form to create a new manufacturer.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::postCreate()
|
||||
* @see ManufacturersController::store()
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCreate()
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return View::make('manufacturers/edit')->with('item', new Manufacturer);
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
return view('manufacturers/edit')->with('item', new Manufacturer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the data for a new manufacturer.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::postCreate()
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postCreate()
|
||||
* Validates and stores the data for a new manufacturer.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::create()
|
||||
* @since [v1.0]
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->name = e(Input::get('name'));
|
||||
$manufacturer->user_id = Auth::user()->id;
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return redirect()->to("admin/settings/manufacturers")->with('success', trans('admin/manufacturers/message.create.success'));
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->name = $request->input('name');
|
||||
$manufacturer->user_id = Auth::user()->id;
|
||||
$manufacturer->url = $request->input('url');
|
||||
$manufacturer->support_url = $request->input('support_url');
|
||||
$manufacturer->support_phone = $request->input('support_phone');
|
||||
$manufacturer->support_email = $request->input('support_email');
|
||||
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = str_slug($image->getClientOriginalName()).".".$image->getClientOriginalExtension();
|
||||
$path = public_path('uploads/manufacturers/'.$file_name);
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save($path);
|
||||
$manufacturer->image = $file_name;
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($manufacturer->getErrors());
|
||||
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.create.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($manufacturer->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays a form to edit a manufacturer.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::postEdit()
|
||||
* @see ManufacturersController::update()
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getEdit($manufacturerId = null)
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function edit($id = null)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($item = Manufacturer::find($manufacturerId))) {
|
||||
// Redirect to the manufacturer page
|
||||
return redirect()->to('admin/settings/manufacturers')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
if (is_null($item = Manufacturer::find($id))) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Show the page
|
||||
return View::make('manufacturers/edit', compact('item'));
|
||||
return view('manufacturers/edit', compact('item'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates and stores the updated manufacturer data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getEdit()
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function postEdit($manufacturerId = null)
|
||||
* Validates and stores the updated manufacturer data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getEdit()
|
||||
* @param Request $request
|
||||
* @param int $manufacturerId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $manufacturerId = null)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
// Check if the manufacturer exists
|
||||
if (is_null($manufacturer = Manufacturer::find($manufacturerId))) {
|
||||
// Redirect to the manufacturer page
|
||||
return redirect()->to('admin/settings/manufacturers')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
}
|
||||
|
||||
// Save the data
|
||||
$manufacturer->name = e(Input::get('name'));
|
||||
$manufacturer->name = $request->input('name');
|
||||
$manufacturer->url = $request->input('url');
|
||||
$manufacturer->support_url = $request->input('support_url');
|
||||
$manufacturer->support_phone = $request->input('support_phone');
|
||||
$manufacturer->support_email = $request->input('support_email');
|
||||
|
||||
// Was it created?
|
||||
if ($manufacturer->save()) {
|
||||
// Redirect to the new manufacturer page
|
||||
return redirect()->to("admin/settings/manufacturers")->with('success', trans('admin/manufacturers/message.update.success'));
|
||||
$old_image = $manufacturer->image;
|
||||
|
||||
// Set the model's image property to null if the image is being deleted
|
||||
if ($request->input('image_delete') == 1) {
|
||||
$manufacturer->image = null;
|
||||
}
|
||||
|
||||
if ($request->file('image')) {
|
||||
$image = $request->file('image');
|
||||
$file_name = $manufacturer->id.'-'.str_slug($image->getClientOriginalName()) . "." . $image->getClientOriginalExtension();
|
||||
|
||||
if ($image->getClientOriginalExtension()!='svg') {
|
||||
Image::make($image->getRealPath())->resize(800, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
$constraint->upsize();
|
||||
})->save(app('manufacturers_upload_path').$file_name);
|
||||
} else {
|
||||
$image->move(app('manufacturers_upload_path'), $file_name);
|
||||
}
|
||||
$manufacturer->image = $file_name;
|
||||
|
||||
}
|
||||
|
||||
if ((($request->file('image')) && (isset($old_image)) && ($old_image!='')) || ($request->input('image_delete') == 1)) {
|
||||
try {
|
||||
unlink(app('manufacturers_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.update.success'));
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($manufacturer->getErrors());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,410 +187,88 @@ class ManufacturersController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getDelete($manufacturerId)
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
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
|
||||
return redirect()->to('admin/settings/manufacturers')->with('error', trans('admin/manufacturers/message.not_found'));
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
|
||||
}
|
||||
|
||||
if ($manufacturer->has_models() > 0) {
|
||||
|
||||
// Redirect to the asset management page
|
||||
return redirect()->to('admin/settings/manufacturers')->with('error', trans('admin/manufacturers/message.assoc_users'));
|
||||
} else {
|
||||
|
||||
// Delete the manufacturer
|
||||
$manufacturer->delete();
|
||||
|
||||
// Redirect to the manufacturers management page
|
||||
return redirect()->to('admin/settings/manufacturers')->with('success', trans('admin/manufacturers/message.delete.success'));
|
||||
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
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 View
|
||||
*/
|
||||
public function getView($manufacturerId = null)
|
||||
* @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::make('manufacturers/view', compact('manufacturer'));
|
||||
} else {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/manufacturers/message.does_not_exist', compact('id'));
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('manufacturers')->with('error', $error);
|
||||
return view('manufacturers/view', compact('manufacturer'));
|
||||
}
|
||||
|
||||
|
||||
$error = trans('admin/manufacturers/message.does_not_exist');
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('manufacturers.index')->with('error', $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the JSON used to display the manufacturer listings.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ManufacturersController::getIndex()
|
||||
* @since [v1.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDatatable()
|
||||
* 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)
|
||||
{
|
||||
$manufacturers = Manufacturer::select(array('id','name'))->with('assets')
|
||||
->whereNull('deleted_at');
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::onlyTrashed()->where('id',$manufacturers_id)->first();
|
||||
|
||||
if (Input::has('search')) {
|
||||
$manufacturers = $manufacturers->TextSearch(e(Input::get('search')));
|
||||
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.');
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$allowed_columns = ['id','name'];
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
|
||||
$manufacturers->orderBy($sort, $order);
|
||||
|
||||
$manufacturersCount = $manufacturers->count();
|
||||
$manufacturers = $manufacturers->skip($offset)->take($limit)->get();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($manufacturers as $manufacturer) {
|
||||
$actions = '<a href="'.route('update/manufacturer', $manufacturer->id).'" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a><a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/manufacturer', $manufacturer->id).'" data-content="'.trans('admin/manufacturers/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($manufacturer->name).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $manufacturer->id,
|
||||
'name' => (string)link_to('admin/settings/manufacturers/'.$manufacturer->id.'/view', e($manufacturer->name)),
|
||||
'assets' => $manufacturer->assets->count(),
|
||||
'actions' => $actions
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $manufacturersCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
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
|
||||
* @since [v1.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function getDataView($manufacturerId, $itemtype = null)
|
||||
{
|
||||
$manufacturer = Manufacturer::find($manufacturerId);
|
||||
|
||||
switch ($itemtype) {
|
||||
case "assets":
|
||||
return $this->getDataAssetsView($manufacturer);
|
||||
case "licenses":
|
||||
return $this->getDataLicensesView($manufacturer);
|
||||
case "accessories":
|
||||
return $this->getDataAccessoriesView($manufacturer);
|
||||
case "consumables":
|
||||
return $this->getDataConsumablesView($manufacturer);
|
||||
}
|
||||
|
||||
throw new Exception("We shouldn't be here");
|
||||
|
||||
}
|
||||
|
||||
protected function getDataAssetsView(Manufacturer $manufacturer)
|
||||
{
|
||||
$manufacturer = $manufacturer->load('assets.model', 'assets.assigneduser', 'assets.assetstatus', 'assets.company');
|
||||
$manufacturer_assets = $manufacturer->assets;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$manufacturer_assets = $manufacturer_assets->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('offset')) {
|
||||
$offset = e(Input::get('offset'));
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$allowed_columns = ['id','name','serial','asset_tag'];
|
||||
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
|
||||
$count = $manufacturer_assets->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($manufacturer_assets as $asset) {
|
||||
|
||||
$actions = '';
|
||||
if ($asset->deleted_at=='') {
|
||||
$actions = '<div style=" white-space: nowrap;"><a href="'.route('clone/hardware', $asset->id).'" class="btn btn-info btn-sm" title="Clone asset"><i class="fa fa-files-o"></i></a> <a href="'.route('update/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-pencil icon-white"></i></a> <a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'.route('delete/hardware', $asset->id).'" data-content="'.trans('admin/hardware/message.delete.confirm').'" data-title="'.trans('general.delete').' '.htmlspecialchars($asset->asset_tag).'?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a></div>';
|
||||
} elseif ($asset->deleted_at!='') {
|
||||
$actions = '<a href="'.route('restore/hardware', $asset->id).'" class="btn btn-warning btn-sm"><i class="fa fa-recycle icon-white"></i></a>';
|
||||
}
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
if (Gate::allows('assets.checkout')) {
|
||||
$inout = '<a href="'.route('checkout/hardware', $asset->id).'" class="btn btn-info btn-sm">'.trans('general.checkout').'</a>';
|
||||
}
|
||||
} else {
|
||||
if (Gate::allows('assets.checkin')) {
|
||||
$inout = '<a href="'.route('checkin/hardware', $asset->id).'" class="btn btn-primary btn-sm">'.trans('general.checkin').'</a>';
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $asset->id,
|
||||
'name' => (string)link_to('/hardware/'.$asset->id.'/view', e($asset->showAssetName())),
|
||||
'model' => e($asset->model->name),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
'assigned_to' => ($asset->assigneduser) ? (string)link_to('/admin/users/'.$asset->assigneduser->id.'/view', e($asset->assigneduser->fullName())): '',
|
||||
'actions' => $actions,
|
||||
// 'companyName' => e(Company::getName($asset)),
|
||||
'companyName' => is_null($asset->company) ? '' : $asset->company->name
|
||||
);
|
||||
|
||||
if (isset($inout)) {
|
||||
$row['change'] = $inout;
|
||||
}
|
||||
}
|
||||
|
||||
$data = array('total' => $count, 'rows' => $rows);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getDataLicensesView(Manufacturer $manufacturer)
|
||||
{
|
||||
$manufacturer = $manufacturer->load('licenses.company', 'licenses.manufacturer', 'licenses.licenseSeatsRelation');
|
||||
$licenses = $manufacturer->licenses;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$licenses = $licenses->TextSearch(Input::get('search'));
|
||||
}
|
||||
|
||||
$licenseCount = $licenses->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($licenses as $license) {
|
||||
$actions = '<span style="white-space: nowrap;">';
|
||||
|
||||
if (Gate::allows('licenses.checkout')) {
|
||||
$actions .= '<a href="' . route('freecheckout/license', $license->id)
|
||||
. '" class="btn btn-primary btn-sm' . (($license->remaincount() > 0) ? '' : ' disabled') . '" style="margin-right:5px;">' . trans('general.checkout') . '</a> ';
|
||||
}
|
||||
|
||||
if (Gate::allows('licenses.create')) {
|
||||
$actions .= '<a href="' . route('clone/license', $license->id)
|
||||
. '" class="btn btn-info btn-sm" style="margin-right:5px;" title="Clone asset"><i class="fa fa-files-o"></i></a>';
|
||||
}
|
||||
if (Gate::allows('licenses.edit')) {
|
||||
$actions .= '<a href="' . route('update/license', $license->id)
|
||||
. '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
}
|
||||
if (Gate::allows('licenses.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="'
|
||||
. route('delete/license', $license->id)
|
||||
. '" data-content="' . trans('admin/licenses/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($license->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
$actions .='</span>';
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $license->id,
|
||||
'name' => (string) link_to('/admin/licenses/'.$license->id.'/view', $license->name),
|
||||
'serial' => (string) link_to('/admin/licenses/'.$license->id.'/view', mb_strimwidth($license->serial, 0, 50, "...")),
|
||||
'totalSeats' => $license->licenseSeatCount,
|
||||
'remaining' => $license->remaincount(),
|
||||
'license_name' => e($license->license_name),
|
||||
'license_email' => e($license->license_email),
|
||||
'purchase_date' => ($license->purchase_date) ? $license->purchase_date : '',
|
||||
'expiration_date' => ($license->expiration_date) ? $license->expiration_date : '',
|
||||
'purchase_cost' => ($license->purchase_cost) ? number_format($license->purchase_cost, 2) : '',
|
||||
'purchase_order' => ($license->purchase_order) ? e($license->purchase_order) : '',
|
||||
'order_number' => ($license->order_number) ? e($license->order_number) : '',
|
||||
'notes' => ($license->notes) ? e($license->notes) : '',
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($license->company) ? '' : e($license->company->name),
|
||||
'manufacturer' => $license->manufacturer ? (string) link_to('/admin/settings/manufacturers/'.$license->manufacturer_id.'/view', $license->manufacturer->name) : ''
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $licenseCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataAccessoriesView(Manufacturer $manufacturer)
|
||||
{
|
||||
$manufacturer = $manufacturer->load(
|
||||
'accessories.location',
|
||||
'accessories.company',
|
||||
'accessories.category',
|
||||
'accessories.manufacturer',
|
||||
'accessories.users'
|
||||
);
|
||||
$accessories = $manufacturer->accessories;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$accessories = $accessories->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$accessCount = $accessories->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
|
||||
$actions = '<nobr>';
|
||||
if (Gate::allows('accessories.checkout')) {
|
||||
$actions .= '<a href="' . route('checkout/accessory',
|
||||
$accessory->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($accessory->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
|
||||
}
|
||||
if (Gate::allows('accessories.edit')) {
|
||||
$actions .= '<a href="' . route('update/accessory',
|
||||
$accessory->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
}
|
||||
if (Gate::allows('accessories.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/accessory',
|
||||
$accessory->id) . '" data-content="' . trans('admin/accessories/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($accessory->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
$actions .= '</nobr>';
|
||||
$company = $accessory->company;
|
||||
|
||||
$rows[] = array(
|
||||
'name' => '<a href="'.url('admin/accessories/'.$accessory->id).'/view">'. $accessory->name.'</a>',
|
||||
'category' => ($accessory->category) ? (string)link_to('admin/settings/categories/'.$accessory->category->id.'/view', $accessory->category->name) : '',
|
||||
'qty' => e($accessory->qty),
|
||||
'order_number' => e($accessory->order_number),
|
||||
'min_amt' => e($accessory->min_amt),
|
||||
'location' => ($accessory->location) ? e($accessory->location->name): '',
|
||||
'purchase_date' => e($accessory->purchase_date),
|
||||
'purchase_cost' => number_format($accessory->purchase_cost, 2),
|
||||
'numRemaining' => $accessory->numRemaining(),
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($company) ? '' : e($company->name),
|
||||
'manufacturer' => $accessory->manufacturer ? (string) link_to('/admin/settings/manufacturers/'.$accessory->manufacturer_id.'/view', $accessory->manufacturer->name) : ''
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total'=>$accessCount, 'rows'=>$rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getDataConsumablesView($manufacturer)
|
||||
{
|
||||
$manufacturer = $manufacturer->load(
|
||||
'consumables.location',
|
||||
'consumables.company',
|
||||
'consumables.category',
|
||||
'consumables.manufacturer',
|
||||
'consumables.users'
|
||||
);
|
||||
$consumables = $manufacturer->consumables;
|
||||
|
||||
if (Input::has('search')) {
|
||||
$consumables = $consumables->TextSearch(e(Input::get('search')));
|
||||
}
|
||||
|
||||
if (Input::has('limit')) {
|
||||
$limit = e(Input::get('limit'));
|
||||
} else {
|
||||
$limit = 50;
|
||||
}
|
||||
|
||||
$consumCount = $consumables->count();
|
||||
|
||||
$rows = array();
|
||||
|
||||
foreach ($consumables as $consumable) {
|
||||
$actions = '<nobr>';
|
||||
if (Gate::allows('consumables.checkout')) {
|
||||
$actions .= '<a href="' . route('checkout/consumable',
|
||||
$consumable->id) . '" style="margin-right:5px;" class="btn btn-info btn-sm" ' . (($consumable->numRemaining() > 0) ? '' : ' disabled') . '>' . trans('general.checkout') . '</a>';
|
||||
}
|
||||
|
||||
if (Gate::allows('consumables.edit')) {
|
||||
$actions .= '<a href="' . route('update/consumable',
|
||||
$consumable->id) . '" class="btn btn-warning btn-sm" style="margin-right:5px;"><i class="fa fa-pencil icon-white"></i></a>';
|
||||
}
|
||||
if (Gate::allows('consumables.delete')) {
|
||||
$actions .= '<a data-html="false" class="btn delete-asset btn-danger btn-sm" data-toggle="modal" href="' . route('delete/consumable',
|
||||
$consumable->id) . '" data-content="' . trans('admin/consumables/message.delete.confirm') . '" data-title="' . trans('general.delete') . ' ' . htmlspecialchars($consumable->name) . '?" onClick="return false;"><i class="fa fa-trash icon-white"></i></a>';
|
||||
}
|
||||
|
||||
$actions .='</nobr>';
|
||||
|
||||
$company = $consumable->company;
|
||||
|
||||
$rows[] = array(
|
||||
'id' => $consumable->id,
|
||||
'name' => (string)link_to('admin/consumables/'.$consumable->id.'/view', e($consumable->name)),
|
||||
'location' => ($consumable->location) ? e($consumable->location->name) : '',
|
||||
'min_amt' => e($consumable->min_amt),
|
||||
'qty' => e($consumable->qty),
|
||||
'manufacturer' => ($consumable->manufacturer) ? (string) link_to('/admin/settings/manufacturers/'.$consumable->manufacturer_id.'/view', $consumable->manufacturer->name): '',
|
||||
'model_number' => e($consumable->model_number),
|
||||
'item_no' => e($consumable->item_no),
|
||||
'category' => ($consumable->category) ? (string) link_to('/admin/settings/categories/'.$consumable->category_id.'/view', $consumable->category->name) : 'Missing category',
|
||||
'order_number' => e($consumable->order_number),
|
||||
'purchase_date' => e($consumable->purchase_date),
|
||||
'purchase_cost' => ($consumable->purchase_cost!='') ? number_format($consumable->purchase_cost, 2): '' ,
|
||||
'numRemaining' => $consumable->numRemaining(),
|
||||
'actions' => $actions,
|
||||
'companyName' => is_null($company) ? '' : e($company->name),
|
||||
);
|
||||
}
|
||||
|
||||
$data = array('total' => $consumCount, 'rows' => $rows);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user