Compare commits
650 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
885ab64c2e | ||
|
|
8624531f78 | ||
|
|
db0c0e7908 | ||
|
|
d77a47765e | ||
|
|
05c0819776 | ||
|
|
16f963fa3d | ||
|
|
e032cf1fda | ||
|
|
10c26f38c4 | ||
|
|
d6b8222371 | ||
|
|
2c5abaaea4 | ||
|
|
c1a0653847 | ||
|
|
9226c8292d | ||
|
|
5fafa81dc1 | ||
|
|
b30d1dce89 | ||
|
|
2dad27eed6 | ||
|
|
f5ffda8053 | ||
|
|
5703b95de3 | ||
|
|
d406dc43c2 | ||
|
|
2ce44bd4e6 | ||
|
|
ed931ef0cd | ||
|
|
bf4ee18123 | ||
|
|
70af10ae6c | ||
|
|
9892e5bf60 | ||
|
|
b9a8d45c07 | ||
|
|
7794c2f44b | ||
|
|
eedc14401a | ||
|
|
4e14d70427 | ||
|
|
2a71690aaf | ||
|
|
e4da00ca82 | ||
|
|
4fd14e5859 | ||
|
|
441f1fbb64 | ||
|
|
bf194d7794 | ||
|
|
d06e3dd892 | ||
|
|
6b25b53462 | ||
|
|
6d79c9f3e2 | ||
|
|
a36957dd77 | ||
|
|
2f3499e4b9 | ||
|
|
3536d08477 | ||
|
|
ee3166cdc2 | ||
|
|
7a117a22c8 | ||
|
|
9a66f6a254 | ||
|
|
5e94726ec1 | ||
|
|
66c3559e1c | ||
|
|
413e44be2f | ||
|
|
ebc35c4519 | ||
|
|
cd963179fd | ||
|
|
796a0ebdaa | ||
|
|
474f24e40e | ||
|
|
b3a0f86431 | ||
|
|
1a31231569 | ||
|
|
f1d4087317 | ||
|
|
f4069e00cd | ||
|
|
8650faf0d8 | ||
|
|
796ef741e8 | ||
|
|
36ae6f9430 | ||
|
|
1945b97b72 | ||
|
|
392e61688d | ||
|
|
db82e06665 | ||
|
|
ac5c6123bc | ||
|
|
8add47739e | ||
|
|
eac8e0bdba | ||
|
|
2815e0d36e | ||
|
|
2e998b110f | ||
|
|
f45c963428 | ||
|
|
2fec40d7df | ||
|
|
215beb9d8a | ||
|
|
b0c61ee044 | ||
|
|
16fb2213b5 | ||
|
|
a0d0645453 | ||
|
|
1ef336a08b | ||
|
|
aa3aa78204 | ||
|
|
db0a078c0b | ||
|
|
1cf1278b3b | ||
|
|
70648dedd3 | ||
|
|
9634dde0dd | ||
|
|
077caa29f8 | ||
|
|
a87478d3ac | ||
|
|
44349db597 | ||
|
|
c70ae19c28 | ||
|
|
55fdc86e02 | ||
|
|
0721ab8bbf | ||
|
|
00c8a1ee21 | ||
|
|
1fc71a4111 | ||
|
|
0c4768fd2a | ||
|
|
f9647614ab | ||
|
|
a05795420a | ||
|
|
42d86bf57b | ||
|
|
201b52baf8 | ||
|
|
f510b9c2a9 | ||
|
|
75bd365ca1 | ||
|
|
153c30eda8 | ||
|
|
be7e6ed847 | ||
|
|
115109f612 | ||
|
|
037a912e21 | ||
|
|
984db1ef44 | ||
|
|
c8fe929e09 | ||
|
|
181dc5127f | ||
|
|
303fc39966 | ||
|
|
730c2a6821 | ||
|
|
bc10761b49 | ||
|
|
cf14a0222c | ||
|
|
b18b3812df | ||
|
|
bb095641c2 | ||
|
|
b78e610ce3 | ||
|
|
884b6b0270 | ||
|
|
7a1ab1292c | ||
|
|
87bb741013 | ||
|
|
5fe2083688 | ||
|
|
2ee84c2675 | ||
|
|
39a5b6b426 | ||
|
|
c6ce928567 | ||
|
|
950a23b0f4 | ||
|
|
b4fac3e4ae | ||
|
|
548e483ef8 | ||
|
|
bad6b862ca | ||
|
|
4e336e11ee | ||
|
|
8588e9ebf1 | ||
|
|
11524d0f7d | ||
|
|
d0bfd8dfd2 | ||
|
|
312200bf44 | ||
|
|
04f4bb83e9 | ||
|
|
9b2dd6522f | ||
|
|
a05fe9c1f7 | ||
|
|
395401e9db | ||
|
|
dbdc1c7f3f | ||
|
|
ace4a00e29 | ||
|
|
c80aa2a289 | ||
|
|
25e2e7ecc6 | ||
|
|
02be4773de | ||
|
|
c988d84271 | ||
|
|
9d5d1a9f9a | ||
|
|
3a7cef15bd | ||
|
|
44d3a425cb | ||
|
|
1854d7d668 | ||
|
|
e5f4048e9e | ||
|
|
5e58f60845 | ||
|
|
a7760b331b | ||
|
|
a419a690d4 | ||
|
|
39b0dc136c | ||
|
|
d0e7879c89 | ||
|
|
acfb41f129 | ||
|
|
5381aa3fbd | ||
|
|
e20a10a6a1 | ||
|
|
949141a8e7 | ||
|
|
e1bf3b50f4 | ||
|
|
cf5e3da3a5 | ||
|
|
1699c09758 | ||
|
|
918e7c8dae | ||
|
|
86afe6c4b1 | ||
|
|
ff97b359ad | ||
|
|
81b66d0039 | ||
|
|
8fa690b635 | ||
|
|
8c1cd87831 | ||
|
|
cde2bad297 | ||
|
|
80d36cd72b | ||
|
|
a579353198 | ||
|
|
7360e15d4e | ||
|
|
9dc2fa61b8 | ||
|
|
80fd49a59e | ||
|
|
d30fa9199c | ||
|
|
8028b39b43 | ||
|
|
ff81e6d536 | ||
|
|
00fad35c2a | ||
|
|
3b68a6f1be | ||
|
|
bc91aef47d | ||
|
|
3debe78574 | ||
|
|
fc1b3b31b5 | ||
|
|
4afd598df7 | ||
|
|
d1d3f893ac | ||
|
|
830d07f84f | ||
|
|
0e30b9aef7 | ||
|
|
b937aedc30 | ||
|
|
55d05eeae3 | ||
|
|
d95d3dc282 | ||
|
|
ab06c26527 | ||
|
|
1ca770895a | ||
|
|
a85fa14f9c | ||
|
|
b058d84f2c | ||
|
|
5fb05d8b1c | ||
|
|
78809c0fe7 | ||
|
|
7ce5993f5a | ||
|
|
f7b483358f | ||
|
|
e75a5f13ec | ||
|
|
cf4e13f4df | ||
|
|
8e7565cbe9 | ||
|
|
4839b0e008 | ||
|
|
d3ddafdff4 | ||
|
|
b07db3b324 | ||
|
|
9ed1442bd1 | ||
|
|
3ea209a507 | ||
|
|
edf98cb795 | ||
|
|
16d18bc7eb | ||
|
|
38c36af6fc | ||
|
|
b5855e7be5 | ||
|
|
0d811d067c | ||
|
|
fba0e2b712 | ||
|
|
608c2f91a8 | ||
|
|
960028b376 | ||
|
|
6690a0f1df | ||
|
|
2c49c32e72 | ||
|
|
76cc46c419 | ||
|
|
dc71f6ddc6 | ||
|
|
7470fdb605 | ||
|
|
930e220cf1 | ||
|
|
2f9e5f79af | ||
|
|
927fba179d | ||
|
|
dfde50732b | ||
|
|
7455318fcf | ||
|
|
17bf899a17 | ||
|
|
a5230319b8 | ||
|
|
8b1c60a17a | ||
|
|
033c3253bb | ||
|
|
a88f622ec3 | ||
|
|
f87ffb84d5 | ||
|
|
bda23bb1e6 | ||
|
|
24c484303e | ||
|
|
5d94b99035 | ||
|
|
c4856c8aed | ||
|
|
0674ef5a3d | ||
|
|
702791210e | ||
|
|
1c77fd0d09 | ||
|
|
d184da8611 | ||
|
|
ac76364140 | ||
|
|
7848a3c3dc | ||
|
|
f41ec640fe | ||
|
|
fc5efd857f | ||
|
|
ccd430ce07 | ||
|
|
f306401e7e | ||
|
|
c06a93ef13 | ||
|
|
17d4e25e60 | ||
|
|
ef6eea67d8 | ||
|
|
84c73aae5d | ||
|
|
dc3af7cc74 | ||
|
|
34eab88b7e | ||
|
|
903609b5a5 | ||
|
|
e491a93892 | ||
|
|
b20c841a89 | ||
|
|
8fe59f8383 | ||
|
|
8bf09d9f89 | ||
|
|
3594ec9905 | ||
|
|
52caee2a9f | ||
|
|
52ea172e5d | ||
|
|
caad5be957 | ||
|
|
fa1cf3073b | ||
|
|
71badee78a | ||
|
|
cb0d1add8d | ||
|
|
1b0d11a572 | ||
|
|
9e522b6a4d | ||
|
|
a773e70936 | ||
|
|
92a38f2a23 | ||
|
|
542f774c68 | ||
|
|
67e106c7fa | ||
|
|
536b5717f0 | ||
|
|
9b48732cd2 | ||
|
|
244616b31e | ||
|
|
22313711d5 | ||
|
|
89e650f842 | ||
|
|
daa88f06f7 | ||
|
|
d0acb9fdb4 | ||
|
|
8e437a66af | ||
|
|
f232579e2b | ||
|
|
7b7583fde3 | ||
|
|
ae466be153 | ||
|
|
f3338667c7 | ||
|
|
f380da3f19 | ||
|
|
919eaf320c | ||
|
|
c9337a1947 | ||
|
|
d069d032fc | ||
|
|
d37dedb654 | ||
|
|
fedf51dda4 | ||
|
|
53334f7905 | ||
|
|
2f9582ee5c | ||
|
|
3b7ce0091c | ||
|
|
6e270c0ed2 | ||
|
|
d0f284129a | ||
|
|
6aa7e9cbfa | ||
|
|
3862b6476b | ||
|
|
7dfab3a6e2 | ||
|
|
0f40ba2b34 | ||
|
|
39a702397a | ||
|
|
a6b3aa5f04 | ||
|
|
3821c4d372 | ||
|
|
104d66b4b1 | ||
|
|
b4a90045e6 | ||
|
|
3b9b63a7a8 | ||
|
|
9b78b25372 | ||
|
|
0411f63591 | ||
|
|
7df4f98e19 | ||
|
|
86a4f2d3ec | ||
|
|
0763c76a4e | ||
|
|
3af7c66de7 | ||
|
|
0be4b21721 | ||
|
|
5d32c17a2e | ||
|
|
10ca7cffc3 | ||
|
|
05faffbd28 | ||
|
|
dacdf788bc | ||
|
|
d54057e495 | ||
|
|
a22c35140b | ||
|
|
61176335d7 | ||
|
|
11c8b1259e | ||
|
|
9b52c61d95 | ||
|
|
2dfb965885 | ||
|
|
36464bc17d | ||
|
|
f35208d58d | ||
|
|
4d30edd535 | ||
|
|
957f33c8cf | ||
|
|
7c8c567eaf | ||
|
|
6772ace94e | ||
|
|
ee28f3e853 | ||
|
|
81f1f4ce6f | ||
|
|
3b79038879 | ||
|
|
7e611fa699 | ||
|
|
e8ad5dc273 | ||
|
|
8a93e1e796 | ||
|
|
3d7000f759 | ||
|
|
d96f877aa4 | ||
|
|
7b665ade0a | ||
|
|
02705d0d1a | ||
|
|
772a06c87a | ||
|
|
0d633ce618 | ||
|
|
cedf77b5ed | ||
|
|
c6b26965a0 | ||
|
|
4b303adda7 | ||
|
|
37fe4e91b1 | ||
|
|
179f26ca2e | ||
|
|
1ae665b645 | ||
|
|
1b433920f1 | ||
|
|
2b64af0d34 | ||
|
|
7f31befe5d | ||
|
|
a5409215fc | ||
|
|
80175cffdc | ||
|
|
f8f969919e | ||
|
|
fb68c49c44 | ||
|
|
514f9aa64a | ||
|
|
0d633688a4 | ||
|
|
949454c6d4 | ||
|
|
d100a5de72 | ||
|
|
acefb3d1b9 | ||
|
|
38a544ea42 | ||
|
|
a4e307c4db | ||
|
|
116bc4ece4 | ||
|
|
14a8baecaa | ||
|
|
f1b8b7d11d | ||
|
|
c6b3fc219c | ||
|
|
cfaa6679af | ||
|
|
bb5a04491d | ||
|
|
f9c0eee7c9 | ||
|
|
27ff0be9a8 | ||
|
|
b5525e6a21 | ||
|
|
c3eb7a3425 | ||
|
|
48374f0854 | ||
|
|
bae3c9ce93 | ||
|
|
b51392e4a5 | ||
|
|
6bd18ebefa | ||
|
|
034eb5fb07 | ||
|
|
563edddfc5 | ||
|
|
391d4f839a | ||
|
|
51a359496a | ||
|
|
43b7c844b0 | ||
|
|
a1674f8d58 | ||
|
|
a8a6950b7e | ||
|
|
4598fcf666 | ||
|
|
37eac18c69 | ||
|
|
ca3296b65a | ||
|
|
a79f49ade3 | ||
|
|
a26c227dcb | ||
|
|
94310e18b1 | ||
|
|
2f25eb598b | ||
|
|
387018c44e | ||
|
|
67357e07f1 | ||
|
|
e621eaf456 | ||
|
|
ea1d7a42e2 | ||
|
|
d5a7955e1d | ||
|
|
e33ab269ae | ||
|
|
4293674f4a | ||
|
|
aeae681326 | ||
|
|
4794f93224 | ||
|
|
f58ed6bd1f | ||
|
|
96c0dba92b | ||
|
|
07a2ef2234 | ||
|
|
d917ae51b7 | ||
|
|
e7470b5545 | ||
|
|
7e2def7896 | ||
|
|
a02534b6c8 | ||
|
|
5c92ddb2c6 | ||
|
|
6d3a82aacf | ||
|
|
da0b375773 | ||
|
|
d1304cc975 | ||
|
|
085be16966 | ||
|
|
127a3e41bd | ||
|
|
4a79c77630 | ||
|
|
e21b21fbde | ||
|
|
137f55e4ce | ||
|
|
4abb9baa95 | ||
|
|
36ddc7dea7 | ||
|
|
80b411c94b | ||
|
|
ae65e2a0a0 | ||
|
|
2d578a9864 | ||
|
|
961e80404a | ||
|
|
830ba470dd | ||
|
|
80fb24e861 | ||
|
|
70f6753f50 | ||
|
|
9285697611 | ||
|
|
9687bcb41c | ||
|
|
4ec4e0f44e | ||
|
|
ff8faab3be | ||
|
|
e28db2d221 | ||
|
|
24af2ab67a | ||
|
|
7b447a2f16 | ||
|
|
f5112b47cd | ||
|
|
4519f6e180 | ||
|
|
6f7718dd0e | ||
|
|
f04e23cacb | ||
|
|
1b66f7f719 | ||
|
|
4207858a14 | ||
|
|
b88fde5dae | ||
|
|
04fa5f2022 | ||
|
|
923d2a79ae | ||
|
|
c970464690 | ||
|
|
f99602c039 | ||
|
|
0aa328f908 | ||
|
|
f8562e5835 | ||
|
|
8a6c7269d3 | ||
|
|
37f2c7beac | ||
|
|
b5bb74b8ca | ||
|
|
b5e69d6678 | ||
|
|
cce808c784 | ||
|
|
4d4badf830 | ||
|
|
d70e4e04c0 | ||
|
|
98285001ac | ||
|
|
29c584289f | ||
|
|
dc79ca94a2 | ||
|
|
ef687fdc7b | ||
|
|
45caa8a90d | ||
|
|
b1e2f86871 | ||
|
|
01037cf9cb | ||
|
|
63a5c70e8e | ||
|
|
3fedcc6766 | ||
|
|
892ae9cf91 | ||
|
|
9ae6591aa3 | ||
|
|
cdc4940338 | ||
|
|
27cdfbc579 | ||
|
|
405545cd88 | ||
|
|
1b8156ac7f | ||
|
|
d8fdd1b408 | ||
|
|
593e1234a5 | ||
|
|
9a5d9eafeb | ||
|
|
eca15bd49b | ||
|
|
d2fc98b685 | ||
|
|
38a2a0c1ee | ||
|
|
75d4a46fff | ||
|
|
0f1c48cb6f | ||
|
|
9fb911146f | ||
|
|
615bdd0499 | ||
|
|
33e92c975a | ||
|
|
5f842d02ef | ||
|
|
f5ba47fcdd | ||
|
|
44612e5eb7 | ||
|
|
6b7d5ed5a4 | ||
|
|
5e76d50f2d | ||
|
|
febf1ec20f | ||
|
|
fa8b0964ed | ||
|
|
a0798a68d9 | ||
|
|
7b12668af4 | ||
|
|
5b5874499d | ||
|
|
4cfdaf89d8 | ||
|
|
b307d2858c | ||
|
|
0ec2884c29 | ||
|
|
f264cade7d | ||
|
|
8ea3acc943 | ||
|
|
f785c3e759 | ||
|
|
0a2a8932d4 | ||
|
|
d2e94dfc1c | ||
|
|
4379ea61fa | ||
|
|
41deabf998 | ||
|
|
7a424649c8 | ||
|
|
b2d3ba7410 | ||
|
|
168d7f7004 | ||
|
|
60b8320b4b | ||
|
|
721b749ae1 | ||
|
|
11d9b1ba45 | ||
|
|
2f10d946ec | ||
|
|
6e0355fa34 | ||
|
|
131a285e2f | ||
|
|
f706c87cbc | ||
|
|
f53cabee24 | ||
|
|
e275c9ee90 | ||
|
|
3d8acd1bd8 | ||
|
|
2169c62700 | ||
|
|
c21b291484 | ||
|
|
c1bc2486ad | ||
|
|
e3166c2209 | ||
|
|
7a5b5c291d | ||
|
|
d66ef233bf | ||
|
|
013df747d7 | ||
|
|
02fb7ac03e | ||
|
|
d48a9d549d | ||
|
|
f59f3dbde4 | ||
|
|
f4fa6836cb | ||
|
|
e846e6ac76 | ||
|
|
e15159b9c3 | ||
|
|
5545457536 | ||
|
|
19994e2097 | ||
|
|
102591b009 | ||
|
|
347e742e88 | ||
|
|
b46e2b5990 | ||
|
|
868419b35b | ||
|
|
df7e0e5630 | ||
|
|
6048acc95d | ||
|
|
86b3f8349a | ||
|
|
fbf5c705db | ||
|
|
b0aa26e6cb | ||
|
|
b49733832c | ||
|
|
8c0be3aa87 | ||
|
|
a936744e2e | ||
|
|
f9da83bc46 | ||
|
|
fc8498972e | ||
|
|
4ba75291e4 | ||
|
|
a7b1e31776 | ||
|
|
604a0b6df1 | ||
|
|
4ed9788a0e | ||
|
|
e8a4059db9 | ||
|
|
aa402bf896 | ||
|
|
8d4219759e | ||
|
|
aba912001d | ||
|
|
15b82997ca | ||
|
|
dce1dd41c8 | ||
|
|
f504d7ef5f | ||
|
|
9ba7e7a0f3 | ||
|
|
30297e479e | ||
|
|
19413a63da | ||
|
|
a4fd0c9c6d | ||
|
|
5b5b70e639 | ||
|
|
6521f16b80 | ||
|
|
9b2cb19f22 | ||
|
|
e0c5205e9b | ||
|
|
695bf1e15f | ||
|
|
0e2efb6573 | ||
|
|
3ca3de9e4f | ||
|
|
174d53aff9 | ||
|
|
22152f0a8c | ||
|
|
b5a75206fe | ||
|
|
3f39cff225 | ||
|
|
a37edd5c5b | ||
|
|
6f3e156be6 | ||
|
|
7d1c2199ed | ||
|
|
f199098a59 | ||
|
|
335f4e50a5 | ||
|
|
1808986bf5 | ||
|
|
bc0c887812 | ||
|
|
d8191f738c | ||
|
|
7b51bf4f51 | ||
|
|
450183c55c | ||
|
|
e44a25126d | ||
|
|
c801305c9b | ||
|
|
4db5a8f62b | ||
|
|
7b4cc5044b | ||
|
|
1c1ebdf44d | ||
|
|
c684e5f481 | ||
|
|
7b5a6a0085 | ||
|
|
1bc4d1b997 | ||
|
|
ac52ea3463 | ||
|
|
a521ef706f | ||
|
|
154adeb9b2 | ||
|
|
491a788cd0 | ||
|
|
a67ce965ec | ||
|
|
3e102bf57b | ||
|
|
cdcb153b1e | ||
|
|
0153c6ae96 | ||
|
|
a330dca7d4 | ||
|
|
ae2c77f97f | ||
|
|
ea7f18d0e6 | ||
|
|
9a429952ff | ||
|
|
8f4b88a877 | ||
|
|
72f27ccc5b | ||
|
|
8cd78b2790 | ||
|
|
aae6a8fc6c | ||
|
|
ace7abc1ad | ||
|
|
c567ad0617 | ||
|
|
4c007ae085 | ||
|
|
54cb6c050a | ||
|
|
851e5ca96e | ||
|
|
103c4325ce | ||
|
|
ebe7c2da87 | ||
|
|
4f6b1bb12d | ||
|
|
bb227cafb2 | ||
|
|
6c1dd81e0a | ||
|
|
9f944ad497 | ||
|
|
01e3296ff3 | ||
|
|
7d5a9180e6 | ||
|
|
9f2b4c721d | ||
|
|
3d008079c9 | ||
|
|
b8d413a6b8 | ||
|
|
30a193502f | ||
|
|
11f1b29db9 | ||
|
|
200d0804ec | ||
|
|
d9f5f1182a | ||
|
|
3f09d17389 | ||
|
|
55555ee233 | ||
|
|
cee6f0d579 | ||
|
|
5b4550a6a8 | ||
|
|
1a7edb3411 | ||
|
|
796c7c8431 | ||
|
|
94c1d36e08 | ||
|
|
e71bba441e | ||
|
|
c3d75d3be3 | ||
|
|
9eea46adef | ||
|
|
19766a0a72 | ||
|
|
4030789786 | ||
|
|
fd082addff | ||
|
|
d636566012 | ||
|
|
6066005aeb | ||
|
|
76897f3a3a | ||
|
|
c1babdc9b0 | ||
|
|
1a95337db1 | ||
|
|
6ed5dff1a5 | ||
|
|
3f559c4a50 | ||
|
|
a29ef73346 | ||
|
|
015ca1fcdc | ||
|
|
ded61614d1 | ||
|
|
62199f6255 | ||
|
|
75b89b5a97 | ||
|
|
a704614397 | ||
|
|
2dcb50d28e | ||
|
|
9aac3ae628 | ||
|
|
397e2df3ea | ||
|
|
4e408cbc42 | ||
|
|
570a31a1c4 | ||
|
|
ece627b3a3 | ||
|
|
d8d3fa2293 | ||
|
|
ab694347b0 | ||
|
|
aa98e4ea7a | ||
|
|
6935d94184 | ||
|
|
d9d5b4d730 | ||
|
|
36a43642d8 | ||
|
|
ed98683496 | ||
|
|
c2dc7ac182 | ||
|
|
37f7768e7a | ||
|
|
c1a8b609ea | ||
|
|
6c1553167d | ||
|
|
c572c98361 | ||
|
|
6c0af3c6e1 | ||
|
|
715b1a0fb2 | ||
|
|
a6bbe1fec3 | ||
|
|
67ac3631df | ||
|
|
5760b76c4e | ||
|
|
57b9b571dc | ||
|
|
62f091e769 | ||
|
|
cee5eea121 | ||
|
|
c6726015f7 |
@@ -2308,13 +2308,247 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nevets82",
|
||||
"name": "Steven",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4462739?v=4",
|
||||
"profile": "http://nevets82.github.io",
|
||||
"login": "JohnnyPicnic",
|
||||
"name": "JohnnyPicnic",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5553884?v=4",
|
||||
"profile": "https://github.com/JohnnyPicnic",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "markbrule",
|
||||
"name": "markbrule",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8799594?v=4",
|
||||
"profile": "https://github.com/markbrule",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mikecmpbll",
|
||||
"name": "Mike Campbell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1962801?v=4",
|
||||
"profile": "https://github.com/mikecmpbll",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tbrconnect",
|
||||
"name": "tbrconnect",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11973217?v=4",
|
||||
"profile": "https://github.com/tbrconnect",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kcoyo",
|
||||
"name": "kcoyo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12447225?v=4",
|
||||
"profile": "https://github.com/kcoyo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "travismiller",
|
||||
"name": "Travis Miller",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/494017?v=4",
|
||||
"profile": "https://travismiller.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PetriAsi",
|
||||
"name": "Petri Asikainen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8735148?v=4",
|
||||
"profile": "https://github.com/PetriAsi",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "derdeagle",
|
||||
"name": "derdeagle",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11424540?v=4",
|
||||
"profile": "https://github.com/derdeagle",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vapier",
|
||||
"name": "Mike Frysinger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/176950?v=4",
|
||||
"profile": "https://wh0rd.org/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AL4AL",
|
||||
"name": "ALPHA",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22044358?v=4",
|
||||
"profile": "https://github.com/AL4AL",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FliegenKLATSCH",
|
||||
"name": "FliegenKLATSCH",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1042587?v=4",
|
||||
"profile": "https://www.ifern.de",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jerm",
|
||||
"name": "Jeremy Price",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/442138?v=4",
|
||||
"profile": "https://github.com/jerm",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Toreg87",
|
||||
"name": "Toreg87",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/84392209?v=4",
|
||||
"profile": "https://github.com/Toreg87",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Computroniks",
|
||||
"name": "Matthew Nickson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/67638596?v=4",
|
||||
"profile": "https://github.com/Computroniks",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jethron",
|
||||
"name": "Jethro Nederhof",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1646397?v=4",
|
||||
"profile": "https://jethron.id.au",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "01ste02",
|
||||
"name": "Oskar Stenberg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23289826?v=4",
|
||||
"profile": "https://github.com/01ste02",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Robert-Azelis",
|
||||
"name": "Robert-Azelis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/82208283?v=4",
|
||||
"profile": "https://github.com/Robert-Azelis",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alwism",
|
||||
"name": "Alexander William Smith",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60648387?v=4",
|
||||
"profile": "https://github.com/alwism",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "leitwerk-ag",
|
||||
"name": "LEITWERK AG",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24418301?v=4",
|
||||
"profile": "https://www.leitwerk.de/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "adamboutcher",
|
||||
"name": "Adam",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1911435?v=4",
|
||||
"profile": "http://www.aboutcher.co.uk",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sneak-it",
|
||||
"name": "Ian",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16104273?v=4",
|
||||
"profile": "https://snksrv.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bestlong",
|
||||
"name": "Shao Yu-Lung (Allen)",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4023909?v=4",
|
||||
"profile": "http://blog.bestlong.idv.tw/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Haxatron",
|
||||
"name": "Haxatron",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/76475453?v=4",
|
||||
"profile": "https://github.com/Haxatron",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "exula",
|
||||
"name": "Bradley Coudriet",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3842948?v=4",
|
||||
"profile": "http://bjcpgd.cias.rit.edu",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "UniversalSuperBox",
|
||||
"name": "Dalton Durst",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/21966173?v=4",
|
||||
"profile": "https://daltondur.st",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "TenOfTens",
|
||||
"name": "TenOfTens",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/48162670?v=4",
|
||||
"profile": "https://github.com/TenOfTens",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "savornicesei",
|
||||
"name": "Simona Avornicesei",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/917232?v=4",
|
||||
"profile": "http://www.avornicesei.com",
|
||||
"contributions": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ ENCRYPT=false
|
||||
COOKIE_NAME=snipeit_session
|
||||
COOKIE_DOMAIN=null
|
||||
SECURE_COOKIES=false
|
||||
API_TOKEN_EXPIRATION_YEARS=40
|
||||
API_TOKEN_EXPIRATION_YEARS=15
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SECURITY HEADER SETTINGS
|
||||
@@ -145,6 +145,7 @@ APP_LOG_MAX_FILES=10
|
||||
APP_LOCKED=false
|
||||
APP_CIPHER=AES-256-CBC
|
||||
APP_FORCE_TLS=false
|
||||
APP_ALLOW_INSECURE_HOSTS=false
|
||||
GOOGLE_MAPS_API=
|
||||
LDAP_MEM_LIM=500M
|
||||
LDAP_TIME_LIM=600
|
||||
|
||||
62
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
62
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -1,62 +0,0 @@
|
||||
---
|
||||
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.
|
||||
- Include the output from `php -m` (this should display what modules you have enabled.)
|
||||
|
||||
**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
23
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
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.
|
||||
129
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
129
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Debug mode
|
||||
description: Please confirm you have done the following before posting your bug report
|
||||
options:
|
||||
- label: I have enabled debug mode
|
||||
required: true
|
||||
- label: I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: Steps to reproduce the behavior.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: 'If applicable, add screenshots to help explain your problem.'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Server"
|
||||
- type: input
|
||||
attributes:
|
||||
label: Snipe-IT Version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: server_operatingSystem
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: 'e.g. Ubuntu, Windows'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Web Server
|
||||
description: 'e.g. Apache, IIS'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: PHP Version
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Desktop"
|
||||
- type: input
|
||||
id: desktop_operatingSystem
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: 'e.g. Ubuntu, Windows'
|
||||
- type: input
|
||||
id: desktop_browser
|
||||
attributes:
|
||||
label: Browser
|
||||
description: 'e.g. Google Chrome, Safari'
|
||||
- type: input
|
||||
id: desktop_version
|
||||
attributes:
|
||||
label: Version
|
||||
description: 'e.g. 93'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### Mobile"
|
||||
- type: input
|
||||
attributes:
|
||||
label: Device
|
||||
description: 'e.g. iPhone 6, Pixel 4a'
|
||||
- type: input
|
||||
id: mobile_operatingSystem
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: 'e.g. iOS 8.1, Android 9'
|
||||
- type: input
|
||||
id: mobile_browser
|
||||
attributes:
|
||||
label: Browser
|
||||
description: 'e.g. Google Chrome, Safari'
|
||||
- type: input
|
||||
id: mobile_version
|
||||
attributes:
|
||||
label: Version
|
||||
description: 'e.g. 93'
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Error messages
|
||||
description: |
|
||||
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.
|
||||
Include the output from `php -m` (this should display what modules you have enabled.)
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: |
|
||||
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.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: 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.
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
42
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Snipe-IT Version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: server_operatingSystem
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: 'e.g. Ubuntu, Windows'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Web Server
|
||||
description: 'e.g. Apache, IIS'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: PHP Version
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 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 [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context Add any other context or screenshots about the feature request here.
|
||||
82
.github/workflows/docker-alpine.yml
vendored
Normal file
82
.github/workflows/docker-alpine.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Snipe-IT (Alpine) Docker image build for hub.docker.com
|
||||
name: Docker images (Alpine)
|
||||
|
||||
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
|
||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
tags:
|
||||
- 'v**'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
|
||||
if: github.repository == 'snipe/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
|
||||
# For a new commit on other branches, use the branch name as the tag for Docker image.
|
||||
# For a new tag, copy that tag name as the tag for Docker image.
|
||||
IMAGE_TAGS: |
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
|
||||
type=ref,event=tag,suffix=-alpine
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
TAGS_FLAVOR: |
|
||||
latest=false
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/checkout
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
###############################################
|
||||
# Build/Push the 'snipe/snipe-it' image
|
||||
###############################################
|
||||
# https://github.com/docker/metadata-action
|
||||
# Get Metadata for docker_build step below
|
||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
|
||||
id: meta_build
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: snipe/snipe-it
|
||||
tags: ${{ env.IMAGE_TAGS }}
|
||||
flavor: ${{ env.TAGS_FLAVOR }}
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push 'snipe-it' image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.alpine
|
||||
platforms: linux/amd64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
# Use tags / labels provided by 'docker/metadata-action' above
|
||||
tags: ${{ steps.meta_build.outputs.tags }}
|
||||
labels: ${{ steps.meta_build.outputs.labels }}
|
||||
82
.github/workflows/docker.yml
vendored
Normal file
82
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Snipe-IT Docker image build for hub.docker.com
|
||||
name: Docker images
|
||||
|
||||
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
|
||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
tags:
|
||||
- 'v**'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
|
||||
if: github.repository == 'snipe/snipe-it'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
||||
# For a new commit on default branch (master), use the literal tag 'latest' on Docker image.
|
||||
# For a new commit on other branches, use the branch name as the tag for Docker image.
|
||||
# For a new tag, copy that tag name as the tag for Docker image.
|
||||
IMAGE_TAGS: |
|
||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
|
||||
type=ref,event=tag
|
||||
# Define default tag "flavor" for docker/metadata-action per
|
||||
# https://github.com/docker/metadata-action#flavor-input
|
||||
# We turn off 'latest' tag by default.
|
||||
TAGS_FLAVOR: |
|
||||
latest=false
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/checkout
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
###############################################
|
||||
# Build/Push the 'snipe/snipe-it' image
|
||||
###############################################
|
||||
# https://github.com/docker/metadata-action
|
||||
# Get Metadata for docker_build step below
|
||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
|
||||
id: meta_build
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: snipe/snipe-it
|
||||
tags: ${{ env.IMAGE_TAGS }}
|
||||
flavor: ${{ env.TAGS_FLAVOR }}
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push 'snipe-it' image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
# Use tags / labels provided by 'docker/metadata-action' above
|
||||
tags: ${{ steps.meta_build.outputs.tags }}
|
||||
labels: ${{ steps.meta_build.outputs.labels }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -63,3 +63,5 @@ _ide_helper.php
|
||||
.phpstorm.meta.php
|
||||
_ide_helper_models.php
|
||||
/.phplint-cache
|
||||
storage/ldap_client_tls.cert
|
||||
storage/ldap_client_tls.key
|
||||
|
||||
36
Dockerfile
36
Dockerfile
@@ -1,5 +1,5 @@
|
||||
FROM ubuntu:bionic
|
||||
LABEL maintainer Brady Wetherington <uberbrady@gmail.com>
|
||||
FROM ubuntu:focal
|
||||
LABEL maintainer Brady Wetherington <bwetherington@grokability.com>
|
||||
|
||||
# No need to add `apt-get clean` here, reference:
|
||||
# - https://github.com/snipe/snipe-it/pull/9201
|
||||
@@ -14,15 +14,15 @@ RUN export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-utils \
|
||||
apache2 \
|
||||
apache2-bin \
|
||||
libapache2-mod-php7.2 \
|
||||
php7.2-curl \
|
||||
php7.2-ldap \
|
||||
php7.2-mysql \
|
||||
php7.2-gd \
|
||||
php7.2-xml \
|
||||
php7.2-mbstring \
|
||||
php7.2-zip \
|
||||
php7.2-bcmath \
|
||||
libapache2-mod-php7.4 \
|
||||
php7.4-curl \
|
||||
php7.4-ldap \
|
||||
php7.4-mysql \
|
||||
php7.4-gd \
|
||||
php7.4-xml \
|
||||
php7.4-mbstring \
|
||||
php7.4-zip \
|
||||
php7.4-bcmath \
|
||||
patch \
|
||||
curl \
|
||||
wget \
|
||||
@@ -38,7 +38,7 @@ autoconf \
|
||||
libc-dev \
|
||||
pkg-config \
|
||||
libmcrypt-dev \
|
||||
php7.2-dev \
|
||||
php7.4-dev \
|
||||
ca-certificates \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
@@ -47,16 +47,16 @@ unzip \
|
||||
RUN curl -L -O https://github.com/pear/pearweb_phars/raw/master/go-pear.phar
|
||||
RUN php go-pear.phar
|
||||
|
||||
RUN pecl install mcrypt-1.0.2
|
||||
RUN pecl install mcrypt-1.0.3
|
||||
|
||||
RUN bash -c "echo extension=/usr/lib/php/20170718/mcrypt.so > /etc/php/7.2/mods-available/mcrypt.ini"
|
||||
RUN bash -c "echo extension=/usr/lib/php/20190902/mcrypt.so > /etc/php/7.4/mods-available/mcrypt.ini"
|
||||
|
||||
RUN phpenmod mcrypt
|
||||
RUN phpenmod gd
|
||||
RUN phpenmod bcmath
|
||||
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.2/apache2/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.2/cli/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/apache2/php.ini
|
||||
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php/7.4/cli/php.ini
|
||||
|
||||
RUN useradd -m --uid 1000 --gid 50 docker
|
||||
|
||||
@@ -77,6 +77,8 @@ COPY . /var/www/html
|
||||
|
||||
RUN a2enmod rewrite
|
||||
|
||||
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
|
||||
|
||||
############ INITIAL APPLICATION SETUP #####################
|
||||
|
||||
WORKDIR /var/www/html
|
||||
@@ -96,6 +98,8 @@ RUN \
|
||||
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
|
||||
&& mkdir -p "/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" \
|
||||
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.cert" "/var/www/html/storage/ldap_client_tls.cert" \
|
||||
&& ln -fs "/var/lib/snipeit/keys/ldap_client_tls.key" "/var/www/html/storage/ldap_client_tls.key" \
|
||||
&& chown docker "/var/lib/snipeit/keys/" \
|
||||
&& chown -h docker "/var/www/html/storage/" \
|
||||
&& chmod +x /var/www/html/artisan \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3
|
||||
# Apache + PHP
|
||||
RUN apk add --no-cache \
|
||||
apache2 \
|
||||
@@ -32,6 +32,8 @@ RUN apk add --no-cache \
|
||||
mysql-client \
|
||||
tini
|
||||
|
||||
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
|
||||
|
||||
# Where apache's PID lives
|
||||
RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2
|
||||
|
||||
@@ -57,7 +59,7 @@ RUN \
|
||||
&& mkdir -p "/var/lib/snipeit/dumps" && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
|
||||
&& mkdir -p "/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 -h "/var/www/html/storage/*.key"
|
||||
&& chown -hR apache "/var/www/html/storage/" \
|
||||
&& chown -R apache "/var/lib/snipeit"
|
||||
|
||||
# Install composer
|
||||
|
||||
@@ -98,5 +98,6 @@ VOLUME [ "/var/lib/snipeit" ]
|
||||
|
||||
COPY --chown=www-data:www-data docker/docker-secrets.env /var/www/html/.env
|
||||
COPY --chmod=655 docker/docker-entrypoint.sh /usr/local/bin/docker-snipeit-entrypoint
|
||||
COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
|
||||
ENTRYPOINT [ "/usr/local/bin/docker-snipeit-entrypoint" ]
|
||||
CMD [ "/usr/local/bin/docker-php-entrypoint", "php-fpm" ]
|
||||
1
Procfile
Normal file
1
Procfile
Normal file
@@ -0,0 +1 @@
|
||||
web: php heroku/startup.php && heroku-php-apache2 public/
|
||||
15
README.md
15
README.md
@@ -1,5 +1,5 @@
|
||||
 [](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://crowdin.com/project/snipe-it) [](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://discord.gg/yZFtShAcKk) [](https://huntr.dev)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
@@ -19,6 +19,8 @@ For instructions on installing and configuring Snipe-IT on your server, check ou
|
||||
|
||||
If you're having trouble with the installation, please check the [Common Issues](https://snipe-it.readme.io/docs/common-issues) and [Getting Help](https://snipe-it.readme.io/docs/getting-help) documentation, and search this repository's open *and* closed issues for help.
|
||||
|
||||
[](https://heroku.com/deploy)
|
||||
|
||||
-----
|
||||
### User's Manual
|
||||
For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.io/docs/overview).
|
||||
@@ -55,11 +57,11 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
|
||||
- [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)
|
||||
- [InQRy -unmaintained-](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
|
||||
- [Snipe-IT plugin for Jira Service Desk](https://marketplace.atlassian.com/apps/1220964/snipe-it-for-jira)
|
||||
- [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).
|
||||
- [Snipe-IT Bulk Edit](https://github.com/bricelabelle/snipe-it-bulkedit) - Google Script files to use Google Sheets as a bulk checkout/checkin/edit tool for Snipe-it.
|
||||
@@ -125,7 +127,10 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") | [<img src="https://avatars.githubusercontent.com/u/70129?v=4" width="110px;"/><br /><sub>Joel Pittet</sub>](https://pittet.ca)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joelpittet "Code") | [<img src="https://avatars.githubusercontent.com/u/792695?v=4" width="110px;"/><br /><sub>Eli Young</sub>](https://elyscape.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=elyscape "Code") | [<img src="https://avatars.githubusercontent.com/u/317015?v=4" width="110px;"/><br /><sub>Raell Dottin</sub>](https://github.com/raelldottin)<br />[💻](https://github.com/snipe/snipe-it/commits?author=raelldottin "Code") | [<img src="https://avatars.githubusercontent.com/u/1446856?v=4" width="110px;"/><br /><sub>Tom Misilo</sub>](https://github.com/misilot)<br />[💻](https://github.com/snipe/snipe-it/commits?author=misilot "Code") | [<img src="https://avatars.githubusercontent.com/u/4496300?v=4" width="110px;"/><br /><sub>David Davenne</sub>](http://david.davenne.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JuustoMestari "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/4462739?v=4" width="110px;"/><br /><sub>Steven</sub>](http://nevets82.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nevets82 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") | [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") | [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/917232?v=4" width="110px;"/><br /><sub>Simona Avornicesei</sub>](http://www.avornicesei.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=savornicesei "Tests") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
- { regexp: '^DB_PASSWORD=', line: 'DB_PASSWORD=vagrant' }
|
||||
- { regexp: '^APP_URL=', line: "APP_URL=http://{{ fqdn }}" }
|
||||
- { regexp: '^APP_ENV=', line: "APP_ENV=development" }
|
||||
- { regexp: '^APP_DEBUG=', line: "APP_ENV=true" }
|
||||
- { regexp: '^APP_DEBUG=', line: "APP_DEBUG=true" }
|
||||
- name: Generate application key
|
||||
shell: "php {{ app_path }}/artisan key:generate --force"
|
||||
- name: Artisan Migrate
|
||||
|
||||
154
app.json
Normal file
154
app.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"name": "Snipe-IT",
|
||||
"description": "Open source asset management.",
|
||||
"keywords": [
|
||||
"asset management",
|
||||
"it asset"
|
||||
],
|
||||
"website": "https://snipeitapp.com/",
|
||||
"repository": "https://github.com/snipe/snipe-it",
|
||||
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
|
||||
"success_url": "/setup",
|
||||
"env": {
|
||||
"APP_ENV": {
|
||||
"description": "Laravel environment mode. Unless developing the application, this should be production.",
|
||||
"value": "production"
|
||||
},
|
||||
"APP_DEBUG": {
|
||||
"description": "Laravel debug mode. Unless developing the application or actively debugging a problem, this should be set to false.",
|
||||
"value": "false"
|
||||
},
|
||||
"APP_KEY": {
|
||||
"description": "A secret key for verifying the integrity of signed cookies. (See either https://snipe-it.readme.io/docs/generate-your-app-key or generate at https://coderstoolbox.online/toolbox/generate-symfony-secret)",
|
||||
"value": ""
|
||||
},
|
||||
"APP_URL": {
|
||||
"description": "URL where your Snipe-IT install will be available at.",
|
||||
"value": "https://your-app-name.herokuapp.com"
|
||||
},
|
||||
"APP_TIMEZONE": {
|
||||
"description": "Which timezone do you want to use for your install? (http://php.net/manual/en/timezones.php)",
|
||||
"value": "UTC"
|
||||
},
|
||||
"APP_LOCALE": {
|
||||
"description": "Which language do you want to use for your install? (https://snipe-it.readme.io/docs/configuration#setting-a-language)",
|
||||
"value": "en"
|
||||
},
|
||||
"MAX_RESULTS": {
|
||||
"description": "The maximum number of search results that can be returned at one time.",
|
||||
"value": "500"
|
||||
},
|
||||
"MAIL_DRIVER": {
|
||||
"description": "Mail driver - Generally SMTP on Heroku - https://snipe-it.readme.io/docs/configuration#required-outgoing-mail-settings",
|
||||
"value": "smtp"
|
||||
},
|
||||
"MAIL_HOST": {
|
||||
"description": "SMTP Server Hostname",
|
||||
"value": "smtp.your.domain.name"
|
||||
},
|
||||
"MAIL_PORT": {
|
||||
"description": "SMTP Server Port",
|
||||
"value": "25"
|
||||
},
|
||||
"MAIL_USERNAME": {
|
||||
"description": "SMTP Server Username",
|
||||
"value": "YOURUSERNAME"
|
||||
},
|
||||
"MAIL_PASSWORD": {
|
||||
"description": "SMTP Server Password",
|
||||
"value": "YOURPASSWORD"
|
||||
},
|
||||
"MAIL_ENCRYPTION": {
|
||||
"description": "Encryption protocol for email sending.",
|
||||
"value": "null"
|
||||
},
|
||||
"MAIL_FROM_ADDR": {
|
||||
"description": "Email from address",
|
||||
"value": "no-reply@domain.name"
|
||||
},
|
||||
"MAIL_FROM_NAME": {
|
||||
"description": "Email from Name",
|
||||
"value": "Snipe-IT"
|
||||
},
|
||||
"MAIL_REPLYTO_ADDR": {
|
||||
"description": "Email Reply-To address",
|
||||
"value": "your@domain.name"
|
||||
},
|
||||
"MAIL_REPLYTO_NAME": {
|
||||
"description": "Email Reply-To Name",
|
||||
"value": "Snipe-IT"
|
||||
},
|
||||
"MAIL_AUTO_EMBED": {
|
||||
"description": "Whether or not to embed images in emails (via CID or base64) versus linking to them.",
|
||||
"value": "true"
|
||||
},
|
||||
"MAIL_AUTO_EMBED_METHOD": {
|
||||
"description": "Method that should be used for attaching inline images.",
|
||||
"value": "base64"
|
||||
},
|
||||
"SESSION_LIFETIME": {
|
||||
"description": "Specify the time in minutes that the session should remain valid.",
|
||||
"value": "12000"
|
||||
},
|
||||
"EXPIRE_ON_CLOSE": {
|
||||
"description": "Specify whether or not the logged in session should be expired when the user closes their browser window.",
|
||||
"value": "false"
|
||||
},
|
||||
"ENCRYPT": {
|
||||
"description": "Specify whether you wish to use encrypted cookies for your Snipe-IT sessions.",
|
||||
"value": "true"
|
||||
},
|
||||
"COOKIE_NAME": {
|
||||
"description": "The name of the cookie set by Snipe-IT for session management.",
|
||||
"value": "snipeit_session"
|
||||
},
|
||||
"COOKIE_DOMAIN": {
|
||||
"description": "The domain name that the session cookie should be sent for.",
|
||||
"value": "your-app-name.herokuapp.com"
|
||||
},
|
||||
"SECURE_COOKIES": {
|
||||
"description": "Should cookies only be sent for HTTPS connections? Generally true on Heroku.",
|
||||
"value": "true"
|
||||
},
|
||||
"LOGIN_MAX_ATTEMPTS": {
|
||||
"description": "The maximum number of failed attempts allowed before the user is throttled.",
|
||||
"value": "5"
|
||||
},
|
||||
"LOGIN_LOCKOUT_DURATION": {
|
||||
"description": "The duration (in seconds) that the user should be blocked from attempting to authenticate again.",
|
||||
"value": "60"
|
||||
},
|
||||
"APP_LOG": {
|
||||
"description": "Driver to send logs to. (errorlog for stderr)",
|
||||
"value": "errorlog"
|
||||
},
|
||||
"ALLOW_IFRAMING": {
|
||||
"description": "Allow Snipe-IT to be loaded using an iFrame?",
|
||||
"value": "false"
|
||||
},
|
||||
"GOOGLE_MAPS_API": {
|
||||
"description": "Include your Google Maps API key here if you'd like Snipe-IT to load maps from Google on your locations and suppliers pages.",
|
||||
"required": false
|
||||
},
|
||||
"BACKUP_ENV": {
|
||||
"description": "Set this to true if you wish to backup your .env file in your Admin > Backups process.",
|
||||
"value": "true"
|
||||
},
|
||||
"ENABLE_HSTS": {
|
||||
"description": "Whether or not to send the HSTS security policy header.",
|
||||
"value": "false"
|
||||
}
|
||||
},
|
||||
"formation": {
|
||||
"web": {
|
||||
"quantity": 1,
|
||||
"size": "free"
|
||||
}
|
||||
},
|
||||
"image": "heroku/php",
|
||||
"addons": [
|
||||
"cleardb:ignite",
|
||||
"heroku-redis:hobby-dev",
|
||||
"papertrail:choklad"
|
||||
]
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class FixDoubleEscape extends Command
|
||||
|
||||
$tables = [
|
||||
'\App\Models\Asset' => ['name'],
|
||||
'\App\Models\License' => ['name'],
|
||||
'\App\Models\License' => ['name', 'license_name'],
|
||||
'\App\Models\Consumable' => ['name'],
|
||||
'\App\Models\Accessory' => ['name'],
|
||||
'\App\Models\Component' => ['name'],
|
||||
@@ -53,7 +53,7 @@ class FixDoubleEscape extends Command
|
||||
'\App\Models\Group' => ['name'],
|
||||
'\App\Models\Department' => ['name'],
|
||||
'\App\Models\Location' => ['name'],
|
||||
'\App\Models\User' => ['first_name', 'last_name'],
|
||||
'\App\Models\User' => ['first_name', 'last_name', 'jobtitle'],
|
||||
];
|
||||
|
||||
$count = array();
|
||||
@@ -69,10 +69,14 @@ class FixDoubleEscape extends Command
|
||||
$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},ENT_QUOTES);
|
||||
$row->save();
|
||||
foreach($classname::where("$field",'LIKE','%;%')->get() as $row) {
|
||||
|
||||
$fixed = html_entity_decode($row->{$field});
|
||||
if ($row->save()) {
|
||||
$this->info('Updating '.$field.' for '.$classname.' to '.$row->{$field}.' to '.$fixed);
|
||||
} else {
|
||||
$this->error('Could NOT update '.$field.' for '.$classname.' to '.$row->{$field}.' to '.$fixed.': '.$row->getErrors());
|
||||
}
|
||||
$count[$classname][$field]++;
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class LdapSync extends Command
|
||||
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
|
||||
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
|
||||
|
||||
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
|
||||
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag;
|
||||
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
|
||||
$ldap_result_email = Setting::getSettings()->ldap_email;
|
||||
$ldap_result_phone = Setting::getSettings()->ldap_phone_field;
|
||||
@@ -170,7 +170,6 @@ class LdapSync extends Command
|
||||
$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") {
|
||||
|
||||
$item = array();
|
||||
$item["username"] = isset($results[$i][$ldap_result_username][0]) ? $results[$i][$ldap_result_username][0] : "";
|
||||
@@ -192,6 +191,7 @@ class LdapSync extends Command
|
||||
|
||||
|
||||
$user = User::where('username', $item["username"])->first();
|
||||
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item["createorupdate"] = 'updated';
|
||||
@@ -199,7 +199,7 @@ class LdapSync extends Command
|
||||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $pass;
|
||||
$user->activated = 0;
|
||||
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
|
||||
$item["createorupdate"] = 'created';
|
||||
}
|
||||
|
||||
@@ -213,8 +213,19 @@ class LdapSync extends Command
|
||||
$user->country = $item["country"];
|
||||
$user->department_id = $department->id;
|
||||
|
||||
// Sync activated state for Active Directory.
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
if ( !empty($ldap_result_active_flag)) { // IF we have an 'active' flag set....
|
||||
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
|
||||
// (Specifically, we don't handle a value of '0.0' correctly)
|
||||
$raw_value = @$results[$i][$ldap_result_active_flag][0];
|
||||
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
$boolean_cast = (bool)$raw_value;
|
||||
|
||||
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
|
||||
|
||||
} elseif ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
|
||||
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
|
||||
|
||||
/* The following is _probably_ the correct logic, but we can't use it because
|
||||
some users may have been dependent upon the previous behavior, and this
|
||||
could cause additional access to be available to users they don't want
|
||||
@@ -240,16 +251,14 @@ class LdapSync extends Command
|
||||
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
|
||||
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'4194816',// 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH
|
||||
'4260352',// 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
|
||||
'1049088',// 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
|
||||
];
|
||||
$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;
|
||||
}
|
||||
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
|
||||
already-existing accounts will be however the administrator has set them */
|
||||
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
@@ -279,7 +288,6 @@ class LdapSync extends Command
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,9 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
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 App\Models\CustomField;
|
||||
use Schema;
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -29,15 +15,14 @@ class PaveIt extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:pave
|
||||
{--soft : Perform a "Soft" Delete, leaving all migrations, table structure, and the first user in place.}';
|
||||
protected $signature = 'snipeit:pave {--force : Skip the interactive yes/no prompt for confirmation}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Pave the database to start over. This should ALMOST NEVER BE USED. (It is primarily a quick tool for developers.)';
|
||||
protected $description = 'Clear the database tables, leaving all migrations, table structure, and the first user in place. (It is primarily a quick tool for developers.) If you want to destroy all tables as well, use php artisan db:wipe.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@@ -56,106 +41,51 @@ class PaveIt extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE DATA IN YOUR DATABASE. \nThere is NO undo. This WILL destroy ALL of your data. \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
|
||||
if ($this->option('soft')) {
|
||||
Accessory::getQuery()->delete();
|
||||
Asset::getQuery()->delete();
|
||||
Category::getQuery()->delete();
|
||||
Company::getQuery()->delete();
|
||||
Component::getQuery()->delete();
|
||||
Consumable::getQuery()->delete();
|
||||
Department::getQuery()->delete();
|
||||
Depreciation::getQuery()->delete();
|
||||
License::getQuery()->delete();
|
||||
LicenseSeat::getQuery()->delete();
|
||||
Location::getQuery()->delete();
|
||||
Manufacturer::getQuery()->delete();
|
||||
AssetModel::getQuery()->delete();
|
||||
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');
|
||||
DB::statement('delete from checkout_acceptances');
|
||||
DB::statement('delete from consumables_users');
|
||||
DB::statement('delete from custom_field_custom_fieldset');
|
||||
DB::statement('delete from custom_fields');
|
||||
DB::statement('delete from custom_fieldsets');
|
||||
DB::statement('delete from components_assets');
|
||||
DB::statement('delete from kits');
|
||||
DB::statement('delete from kits_accessories');
|
||||
DB::statement('delete from kits_consumables');
|
||||
DB::statement('delete from kits_licenses');
|
||||
DB::statement('delete from kits_models');
|
||||
DB::statement('delete from login_attempts');
|
||||
DB::statement('delete from models_custom_fields');
|
||||
DB::statement('delete from permission_groups');
|
||||
DB::statement('delete from password_resets');
|
||||
DB::statement('delete from requested_assets');
|
||||
DB::statement('delete from requests');
|
||||
DB::statement('delete from throttle');
|
||||
DB::statement('delete from users_groups');
|
||||
DB::statement('delete from users WHERE id!=1');
|
||||
} else {
|
||||
\DB::statement('drop table IF EXISTS accessories_users');
|
||||
\DB::statement('drop table IF EXISTS accessories');
|
||||
\DB::statement('drop table IF EXISTS asset_logs');
|
||||
\DB::statement('drop table IF EXISTS action_logs');
|
||||
\DB::statement('drop table IF EXISTS asset_maintenances');
|
||||
\DB::statement('drop table IF EXISTS asset_uploads');
|
||||
\DB::statement('drop table IF EXISTS assets');
|
||||
\DB::statement('drop table IF EXISTS categories');
|
||||
\DB::statement('drop table IF EXISTS checkout_requests');
|
||||
\DB::statement('drop table IF EXISTS checkout_acceptances');
|
||||
\DB::statement('drop table IF EXISTS companies');
|
||||
\DB::statement('drop table IF EXISTS components');
|
||||
\DB::statement('drop table IF EXISTS components_assets');
|
||||
\DB::statement('drop table IF EXISTS consumables_users');
|
||||
\DB::statement('drop table IF EXISTS consumables');
|
||||
\DB::statement('drop table IF EXISTS custom_field_custom_fieldset');
|
||||
\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 kits');
|
||||
\DB::statement('drop table IF EXISTS kits_accessories');
|
||||
\DB::statement('drop table IF EXISTS kits_consumables');
|
||||
\DB::statement('drop table IF EXISTS kits_licenses');
|
||||
\DB::statement('drop table IF EXISTS kits_models');
|
||||
\DB::statement('drop table IF EXISTS models_custom_fields');
|
||||
\DB::statement('drop table IF EXISTS permission_groups');
|
||||
\DB::statement('drop table IF EXISTS license_seats');
|
||||
\DB::statement('drop table IF EXISTS licenses');
|
||||
\DB::statement('drop table IF EXISTS locations');
|
||||
\DB::statement('drop table IF EXISTS login_attempts');
|
||||
\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');
|
||||
\DB::statement('drop table IF EXISTS settings');
|
||||
\DB::statement('drop table IF EXISTS status_labels');
|
||||
\DB::statement('drop table IF EXISTS suppliers');
|
||||
\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');
|
||||
if (!$this->option('force')) {
|
||||
$confirmation = $this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE DATA IN YOUR DATABASE. \nThere is NO undo. This WILL destroy ALL of your data, \nINCLUDING ANY non-Snipe-IT tables you have in this database. \n****************************************************\n\nDo you wish to continue? No backsies! ");
|
||||
if (!$confirmation) {
|
||||
$this->error('ABORTING');
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// List all the tables in the database so we don't have to worry about missing some as the app grows
|
||||
$tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();
|
||||
|
||||
$except_tables = [
|
||||
'oauth_access_tokens',
|
||||
'oauth_clients',
|
||||
'oauth_personal_access_clients',
|
||||
'migrations',
|
||||
'settings',
|
||||
'users',
|
||||
];
|
||||
|
||||
// We only need to find out what these are so we can nuke these columns on the assets table.
|
||||
$custom_fields = CustomField::get();
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
|
||||
|
||||
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
|
||||
\Schema::table('assets', function ($table) use ($custom_field) {
|
||||
$table->dropColumn($custom_field->db_column);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tables as $table) {
|
||||
if (in_array($table, $except_tables)) {
|
||||
$this->info($table. ' is SKIPPED.');
|
||||
} else {
|
||||
\DB::statement('truncate '.$table);
|
||||
$this->info($table. ' is TRUNCATED.');
|
||||
}
|
||||
}
|
||||
|
||||
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
|
||||
\DB::statement('delete from oauth_clients WHERE id > 2');
|
||||
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,8 @@ class ResetDemoSettings extends Command
|
||||
$user->save();
|
||||
}
|
||||
|
||||
\Storage::disk('public')->put('snipe-logo.png', file_get_contents(public_path('img/demo/snipe-logo.png')));
|
||||
\Storage::disk('public')->put('snipe-logo-lg.png', file_get_contents(public_path('img/demo/snipe-logo-lg.png')));
|
||||
|
||||
}
|
||||
|
||||
|
||||
297
app/Console/Commands/RestoreFromBackup.php
Normal file
297
app/Console/Commands/RestoreFromBackup.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use ZipArchive;
|
||||
|
||||
class RestoreFromBackup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:restore
|
||||
{--force : Skip the danger prompt; assuming you hit "y"}
|
||||
{filename : The zip file to be migrated}
|
||||
{--no-progress : Don\'t show a progress bar}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Restore from a previously created backup';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$dir = getcwd();
|
||||
print "Current working directory is: $dir\n";
|
||||
//
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
if (!$filename) {
|
||||
return $this->error("Missing required filename");
|
||||
}
|
||||
|
||||
if (!$this->option('force') && !$this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
|
||||
return $this->error("Data loss not confirmed");
|
||||
}
|
||||
|
||||
if (config('database.default') != 'mysql') {
|
||||
return $this->error("DB_CONNECTION must be MySQL in order to perform a restore. Detected: ".config('database.default'));
|
||||
}
|
||||
|
||||
$za = new ZipArchive();
|
||||
|
||||
$errcode = $za->open($filename/* , ZipArchive::RDONLY */); // that constant only exists in PHP 7.4 and higher
|
||||
if ($errcode !== true) {
|
||||
$errors = [
|
||||
ZipArchive::ER_EXISTS => "File already exists.",
|
||||
ZipArchive::ER_INCONS => "Zip archive inconsistent.",
|
||||
ZipArchive::ER_INVAL => "Invalid argument.",
|
||||
ZipArchive::ER_MEMORY => "Malloc failure.",
|
||||
ZipArchive::ER_NOENT => "No such file.",
|
||||
ZipArchive::ER_NOZIP => "Not a zip archive.",
|
||||
ZipArchive::ER_OPEN => "Can't open file.",
|
||||
ZipArchive::ER_READ => "Read error.",
|
||||
ZipArchive::ER_SEEK => "Seek error."
|
||||
];
|
||||
|
||||
return $this->error("Could not access file: ".$filename." - ".array_key_exists($errcode,$errors) ? $errors[$errcode] : " Unknown reason: $errcode");
|
||||
}
|
||||
|
||||
|
||||
$private_dirs = [
|
||||
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
|
||||
'storage/private_uploads/audits',
|
||||
'storage/private_uploads/imports',
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/users',
|
||||
'storage/private_uploads/licenses',
|
||||
'storage/private_uploads/signatures'
|
||||
];
|
||||
$private_files = [
|
||||
'storage/oauth-private.key',
|
||||
'storage/oauth-public.key'
|
||||
];
|
||||
$public_dirs = [
|
||||
'public/uploads/companies',
|
||||
'public/uploads/components',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/manufacturers',
|
||||
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
|
||||
'public/uploads/consumables',
|
||||
'public/uploads/departments',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/suppliers',
|
||||
'public/uploads/assets', // these are asset _pictures_, not asset files
|
||||
'public/uploads/locations',
|
||||
'public/uploads/accessories',
|
||||
'public/uploads/models',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/manufacturers'
|
||||
];
|
||||
|
||||
$public_files = [
|
||||
'public/uploads/logo.*',
|
||||
'public/uploads/setting-email_logo*',
|
||||
'public/uploads/setting-label_logo*',
|
||||
'public/uploads/setting-logo*',
|
||||
'public/uploads/favicon.*',
|
||||
'public/uploads/favicon-uploaded.*'
|
||||
];
|
||||
|
||||
$all_files = $private_dirs + $public_dirs;
|
||||
|
||||
$sqlfiles = [];
|
||||
$sqlfile_indices = [];
|
||||
|
||||
$interesting_files = [];
|
||||
$boring_files = [];
|
||||
|
||||
for ($i=0; $i<$za->numFiles;$i++) {
|
||||
$stat_results = $za->statIndex($i);
|
||||
// echo "index: $i\n";
|
||||
// print_r($stat_results);
|
||||
|
||||
$raw_path = $stat_results['name'];
|
||||
if(strpos($raw_path,'\\')!==false) { //found a backslash, swap it to forward-slash
|
||||
$raw_path = strtr($raw_path,'\\','/');
|
||||
//print "Translating file: ".$stat_results['name']." to: ".$raw_path."\n";
|
||||
}
|
||||
|
||||
// skip macOS resource fork files (?!?!?!)
|
||||
if(strpos($raw_path,"__MACOSX")!==false && strpos($raw_path,"._") !== false) {
|
||||
//print "SKIPPING macOS Resource fork file: $raw_path\n";
|
||||
$boring_files[] = $raw_path;
|
||||
continue;
|
||||
}
|
||||
if(@pathinfo($raw_path)['extension'] == "sql") {
|
||||
print "Found a sql file!\n";
|
||||
$sqlfiles[] = $raw_path;
|
||||
$sqlfile_indices[] = $i;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(array_merge($private_dirs,$public_dirs) as $dir) {
|
||||
$last_pos = strrpos($raw_path,$dir.'/');
|
||||
if($last_pos !== false ) {
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
|
||||
$interesting_files[$raw_path] = ['dest' =>$dir, 'index' => $i];
|
||||
continue 2;
|
||||
if($last_pos + strlen($dir) +1 == strlen($raw_path)) {
|
||||
// we don't care about that; we just want files with the appropriate prefix
|
||||
//print("FOUND THE EXACT DIRECTORY: $dir AT: $raw_path!!!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
$good_extensions = ["png","gif","jpg","svg","jpeg","doc","docx","pdf","txt",
|
||||
"zip","rar","xls","xlsx","lic","xml","rtf", "webp","key","ico"];
|
||||
foreach(array_merge($private_files, $public_files) as $file) {
|
||||
$has_wildcard = (strpos($file,"*") !== false);
|
||||
if($has_wildcard) {
|
||||
$file = substr($file,0,-1); //trim last character (which should be the wildcard)
|
||||
}
|
||||
$last_pos = strrpos($raw_path,$file); // no trailing slash!
|
||||
if($last_pos !== false ) {
|
||||
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
||||
if(!in_array($extension, $good_extensions)) {
|
||||
$this->warn("Potentially unsafe file ".$raw_path." is being skipped");
|
||||
$boring_files[] = $raw_path;
|
||||
continue 2;
|
||||
}
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//no wildcards found in $file, process 'normally'
|
||||
if($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
|
||||
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
|
||||
$interesting_files[$raw_path] = ['dest' => dirname($file),'index' => $i];
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$boring_files[] = $raw_path; //if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
|
||||
} // end of pre-processing the ZIP file for-loop
|
||||
|
||||
// print_r($interesting_files);exit(-1);
|
||||
|
||||
if( count($sqlfiles) != 1) {
|
||||
return $this->error("There should be exactly *one* sql backup file found, found: ".( count($sqlfiles) == 0 ? "None" : implode(", ",$sqlfiles)));
|
||||
}
|
||||
|
||||
if( strpos($sqlfiles[0], "db-dumps") === false ) {
|
||||
//return $this->error("SQL backup file is missing 'db-dumps' component of full pathname: ".$sqlfiles[0]);
|
||||
//older Snipe-IT installs don't have the db-dumps subdirectory component
|
||||
}
|
||||
|
||||
//how to invoke the restore?
|
||||
$pipes = [];
|
||||
|
||||
$env_vars = getenv();
|
||||
$env_vars['MYSQL_PWD'] = config("database.connections.mysql.password");
|
||||
$proc_results = proc_open("mysql -h ".escapeshellarg(config('database.connections.mysql.host'))." -u ".escapeshellarg(config('database.connections.mysql.username'))." ".escapeshellarg(config('database.connections.mysql.database')), // yanked -p since we pass via ENV
|
||||
[0 => ['pipe','r'],1 => ['pipe','w'],2 => ['pipe','w']],
|
||||
$pipes,
|
||||
null,
|
||||
$env_vars); // this is not super-duper awesome-secure, but definitely more secure than showing it on the CLI, or dropping temporary files with passwords in them.
|
||||
if($proc_results === false) {
|
||||
return $this->error("Unable to invoke mysql via CLI");
|
||||
}
|
||||
|
||||
// $this->info("Stdout says? ".fgets($pipes[1])); //FIXME: I think we might need to set non-blocking mode to use this properly?
|
||||
// $this->info("Stderr says? ".fgets($pipes[2])); //FIXME: ditto, same.
|
||||
// should we read stdout?
|
||||
// fwrite($pipes[0],config("database.connections.mysql.password")."\n"); //this doesn't work :(
|
||||
|
||||
//$sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
|
||||
|
||||
$sql_stat = $za->statIndex($sqlfile_indices[0]);
|
||||
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
|
||||
$sql_contents = $za->getStream($sql_stat['name']);
|
||||
if ($sql_contents === false) {
|
||||
$stdout = fgets($pipes[1]);
|
||||
$this->info($stdout);
|
||||
$stderr = fgets($pipes[2]);
|
||||
$this->info($stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
while(($buffer = fgets($sql_contents)) !== false ) {
|
||||
//$this->info("Buffer is: '$buffer'");
|
||||
$bytes_written = fwrite($pipes[0],$buffer);
|
||||
if($bytes_written === false) {
|
||||
$stdout = fgets($pipes[1]);
|
||||
$this->info($stdout);
|
||||
$stderr = fgets($pipes[2]);
|
||||
$this->info($stderr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fclose($pipes[0]);
|
||||
fclose($sql_contents);
|
||||
|
||||
$this->line(stream_get_contents($pipes[1]));
|
||||
fclose($pipes[1]);
|
||||
|
||||
$this->error(stream_get_contents($pipes[2]));
|
||||
fclose($pipes[2]);
|
||||
|
||||
//wait, have to do fclose() on all pipes first?
|
||||
$close_results = proc_close($proc_results);
|
||||
if($close_results != 0) {
|
||||
return $this->error("There may have been a problem with the database import: Error number ".$close_results);
|
||||
}
|
||||
|
||||
//and now copy the files over too (right?)
|
||||
//FIXME - we don't prune the filesystem space yet!!!!
|
||||
if($this->option('no-progress')) {
|
||||
$bar = null;
|
||||
} else {
|
||||
$bar = $this->output->createProgressBar(count($interesting_files));
|
||||
}
|
||||
foreach($interesting_files AS $pretty_file_name => $file_details) {
|
||||
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
|
||||
$fp = $za->getStream($ugly_file_name);
|
||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||
$migrated_file = fopen($file_details['dest']."/".basename($pretty_file_name),"w");
|
||||
while(($buffer = fgets($fp))!== false) {
|
||||
fwrite($migrated_file,$buffer);
|
||||
}
|
||||
fclose($migrated_file);
|
||||
fclose($fp);
|
||||
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
|
||||
if($bar) {
|
||||
$bar->advance();
|
||||
}
|
||||
}
|
||||
if($bar) {
|
||||
$bar->finish();
|
||||
$this->line("");
|
||||
} else {
|
||||
$this->info(count($interesting_files)." files were succesfully transferred");
|
||||
}
|
||||
foreach($boring_files as $boring_file) {
|
||||
$this->warn($boring_file." was skipped.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Log;
|
||||
use JsonException;
|
||||
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
@@ -26,6 +27,7 @@ class Handler extends ExceptionHandler
|
||||
\Illuminate\Validation\ValidationException::class,
|
||||
\Intervention\Image\Exception\NotSupportedException::class,
|
||||
\League\OAuth2\Server\Exception\OAuthServerException::class,
|
||||
JsonException::class
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -60,6 +62,12 @@ class Handler extends ExceptionHandler
|
||||
return redirect()->back()->with('error', trans('general.token_expired'));
|
||||
}
|
||||
|
||||
// Invalid JSON exception
|
||||
// TODO: don't understand why we have to do this when we have the invalidJson() method, below, but, well, whatever
|
||||
if ($e instanceof JsonException) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'invalid JSON'), 422);
|
||||
}
|
||||
|
||||
|
||||
// Handle Ajax requests that fail because the model doesn't exist
|
||||
if ($request->ajax() || $request->wantsJson()) {
|
||||
@@ -121,6 +129,6 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
protected function invalidJson($request, ValidationException $exception)
|
||||
{
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors(), 400));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors(), 422));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ class Helper
|
||||
public static function formatCurrencyOutput($cost)
|
||||
{
|
||||
if (is_numeric($cost)) {
|
||||
return number_format($cost, 2, '.', '');
|
||||
if (Setting::getSettings()->digit_separator=='1.234,56') {
|
||||
return number_format($cost, 2, ',', '.');
|
||||
}
|
||||
return number_format($cost, 2, '.', ',');
|
||||
}
|
||||
// It's already been parsed.
|
||||
return $cost;
|
||||
@@ -64,7 +67,7 @@ class Helper
|
||||
{
|
||||
$colors = [
|
||||
"#008941",
|
||||
"#FF4A46",
|
||||
"#FF851B",
|
||||
"#006FA6",
|
||||
"#A30059",
|
||||
"#1CE6FF",
|
||||
@@ -400,6 +403,19 @@ class Helper
|
||||
*/
|
||||
public static function ParseFloat($floatString)
|
||||
{
|
||||
/*******
|
||||
*
|
||||
* WARNING: This does conversions based on *locale* - a Unix-ey-like thing.
|
||||
*
|
||||
* Everything else in the system tends to convert based on the Snipe-IT settings
|
||||
*
|
||||
* So it's very likely this is *not* what you want - instead look for the new
|
||||
*
|
||||
* ParseCurrency($currencyString)
|
||||
*
|
||||
* Which should be directly below here
|
||||
*
|
||||
*/
|
||||
$LocaleInfo = localeconv();
|
||||
$floatString = str_replace(",", "", $floatString);
|
||||
$floatString = str_replace($LocaleInfo["decimal_point"], ".", $floatString);
|
||||
@@ -413,6 +429,26 @@ class Helper
|
||||
$floatString = str_replace($currencySymbol, '', $floatString);
|
||||
return floatval($floatString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format currency using comma or period for thousands, and period or comma for decimal, based on settings.
|
||||
*
|
||||
* @author [B. Wetherington] [<bwetherington@grokability.com>]
|
||||
* @since [v5.2]
|
||||
* @return Float
|
||||
*/
|
||||
public static function ParseCurrency($currencyString) {
|
||||
$without_currency = str_replace(Setting::getSettings()->default_currency, '', $currencyString); //generally shouldn't come up, since we don't do this in fields, but just in case it does...
|
||||
if(Setting::getSettings()->digit_separator=='1.234,56') {
|
||||
//EU format
|
||||
$without_thousands = str_replace('.', '', $without_currency);
|
||||
$corrected_decimal = str_replace(',', '.', $without_thousands);
|
||||
} else {
|
||||
$without_thousands = str_replace(',', '', $without_currency);
|
||||
$corrected_decimal = $without_thousands; // decimal is already OK
|
||||
}
|
||||
return floatval($corrected_decimal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of status labels in an array to make a dropdown menu
|
||||
@@ -887,7 +923,8 @@ class Helper
|
||||
// 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) {
|
||||
|
||||
if ($upload_max > 0 && $upload_max < $post_max_size) {
|
||||
$max_size = ini_get('upload_max_filesize');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class AccessoriesController extends Controller
|
||||
$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->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->user_id = Auth::user()->id;
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
@@ -137,7 +137,7 @@ class AccessoriesController extends Controller
|
||||
$accessory->order_number = request('order_number');
|
||||
$accessory->model_number = request('model_number');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = request('purchase_cost');
|
||||
$accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use Carbon\Carbon;
|
||||
use Auth;
|
||||
use DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
class AccessoriesController extends Controller
|
||||
{
|
||||
@@ -26,9 +27,23 @@ class AccessoriesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
|
||||
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
|
||||
// Relations will be handled in query scopes a little further down.
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'model_number',
|
||||
'eol',
|
||||
'notes',
|
||||
'created_at',
|
||||
'min_amt',
|
||||
'company_id'
|
||||
];
|
||||
|
||||
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
@@ -50,6 +65,10 @@ class AccessoriesController extends Controller
|
||||
$accessories->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$accessories->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
|
||||
@@ -57,24 +76,32 @@ class AccessoriesController extends Controller
|
||||
// Check to make sure the limit is not higher than the max allowed
|
||||
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
|
||||
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$sort_override = $request->input('sort');
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
switch ($sort_override) {
|
||||
case 'category':
|
||||
$accessories = $accessories->OrderCategory($order);
|
||||
break;
|
||||
case 'company':
|
||||
$accessories = $accessories->OrderCompany($order);
|
||||
break;
|
||||
case 'location':
|
||||
$accessories = $accessories->OrderLocation($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$accessories = $accessories->OrderManufacturer($order);
|
||||
break;
|
||||
case 'supplier':
|
||||
$accessories = $accessories->OrderSupplier($order);
|
||||
break;
|
||||
default:
|
||||
$accessories = $accessories->orderBy($sort, $order);
|
||||
$accessories = $accessories->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$accessories->orderBy($sort, $order);
|
||||
|
||||
|
||||
$total = $accessories->count();
|
||||
$accessories = $accessories->skip($offset)->take($limit)->get();
|
||||
return (new AccessoriesTransformer)->transformAccessories($accessories, $total);
|
||||
@@ -86,14 +113,15 @@ class AccessoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Accessory::class);
|
||||
$accessory = new Accessory;
|
||||
$accessory->fill($request->all());
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
if ($accessory->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.create.success')));
|
||||
@@ -165,9 +193,13 @@ class AccessoriesController extends Controller
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessory_users = $accessory->users()
|
||||
->where('first_name', 'like', '%'.$request->input('search').'%')
|
||||
->orWhere('last_name', 'like', '%'.$request->input('search').'%')
|
||||
->get();
|
||||
->where(function ($query) use ($request) {
|
||||
$search_str = '%' . $request->input('search') . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
})
|
||||
->get();
|
||||
$total = $accessory_users->count();
|
||||
}
|
||||
|
||||
@@ -180,15 +212,16 @@ class AccessoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Accessory::class);
|
||||
$accessory = Accessory::findOrFail($id);
|
||||
$accessory->fill($request->all());
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
if ($accessory->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $accessory, trans('admin/accessories/message.update.success')));
|
||||
@@ -287,7 +320,7 @@ class AccessoriesController extends Controller
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessoryUserId), $request->input('note'));
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_user->user_id), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
|
||||
@@ -105,7 +105,7 @@ class AssetMaintenancesController extends Controller
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = e($request->input('cost'));
|
||||
$assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
@@ -162,7 +162,7 @@ class AssetMaintenancesController extends Controller
|
||||
|
||||
$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->cost = Helper::ParseCurrency($request->input('cost'));
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
|
||||
|
||||
if ($request->filled('status')) {
|
||||
if ($request->input('status')=='deleted') {
|
||||
$assetmodels->onlyTrashed();
|
||||
}
|
||||
|
||||
@@ -107,14 +108,15 @@ class AssetModelsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$assetmodel = new AssetModel;
|
||||
$assetmodel->fill($request->all());
|
||||
$assetmodel = $request->handleImages($assetmodel);
|
||||
|
||||
if ($assetmodel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
|
||||
@@ -159,16 +161,17 @@ class AssetModelsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', AssetModel::class);
|
||||
$assetmodel = AssetModel::findOrFail($id);
|
||||
$assetmodel->fill($request->all());
|
||||
|
||||
$assetmodel = $request->handleImages($assetmodel);
|
||||
|
||||
/**
|
||||
* Allow custom_fieldset_id to override and populate fieldset_id.
|
||||
* This is stupid, but required for legacy API support.
|
||||
@@ -231,6 +234,7 @@ class AssetModelsController extends Controller
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$assetmodels = AssetModel::select([
|
||||
'models.id',
|
||||
'models.name',
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\DepreciationReportTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
@@ -20,12 +23,14 @@ use Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Input;
|
||||
use Paginator;
|
||||
use Slack;
|
||||
use Str;
|
||||
use TCPDF;
|
||||
use Validator;
|
||||
use Route;
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,10 +51,33 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request, $audit = null)
|
||||
public function index(Request $request, $audit = null)
|
||||
{
|
||||
|
||||
$this->authorize('index', Asset::class);
|
||||
\Log::debug(Route::currentRouteName());
|
||||
$filter_non_deprecable_assets = false;
|
||||
|
||||
/**
|
||||
* This looks MAD janky (and it is), but the AssetsController@index does a LOT of heavy lifting throughout the
|
||||
* app. This bit here just makes sure that someone without permission to view assets doesn't
|
||||
* end up with priv escalations because they asked for a different endpoint.
|
||||
*
|
||||
* Since we never gave the specification for which transformer to use before, it should default
|
||||
* gracefully to just use the AssetTransformer by default, which shouldn't break anything.
|
||||
*
|
||||
* It was either this mess, or repeating ALL of the searching and sorting and filtering code,
|
||||
* which would have been far worse of a mess. *sad face* - snipe (Sept 1, 2021)
|
||||
*/
|
||||
if (Route::currentRouteName()=='api.depreciation-report.index') {
|
||||
$filter_non_deprecable_assets = true;
|
||||
$transformer = 'App\Http\Transformers\DepreciationReportTransformer';
|
||||
$this->authorize('reports.view');
|
||||
} else {
|
||||
$transformer = 'App\Http\Transformers\AssetsTransformer';
|
||||
$this->authorize('index', Asset::class);
|
||||
}
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$allowed_columns = [
|
||||
@@ -88,13 +116,28 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
||||
|
||||
|
||||
if ($filter_non_deprecable_assets) {
|
||||
$non_deprecable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
|
||||
$assets->InModelList($non_deprecable_models->toArray());
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name())) {
|
||||
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('status_id')) {
|
||||
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
||||
}
|
||||
@@ -243,7 +286,7 @@ class AssetsController extends Controller
|
||||
$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.
|
||||
@@ -292,8 +335,25 @@ class AssetsController extends Controller
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
// dd($assets);
|
||||
return (new AssetsTransformer)->transformAssets($assets, $total);
|
||||
|
||||
|
||||
/**
|
||||
* Include additional associated relationships
|
||||
*/
|
||||
if ($request->input('components')) {
|
||||
$assets->loadMissing(['components' => function ($query) {
|
||||
$query->orderBy('created_at', 'desc');
|
||||
}]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Here we're just determining which Transformer (via $transformer) to use based on the
|
||||
* variables we set earlier on in this method - we default to AssetsTransformer.
|
||||
*/
|
||||
return (new $transformer)->transformAssets($assets, $total, $request);
|
||||
}
|
||||
|
||||
|
||||
@@ -305,11 +365,11 @@ class AssetsController extends Controller
|
||||
* @since [v4.2.1]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showByTag($tag)
|
||||
public function showByTag(Request $request, $tag)
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')->with('assignedTo')->where('asset_tag',$tag)->first()) {
|
||||
$this->authorize('view', $asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
@@ -323,18 +383,24 @@ class AssetsController extends Controller
|
||||
* @since [v4.2.1]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showBySerial($serial)
|
||||
public function showBySerial(Request $request, $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());
|
||||
|
||||
$assets = Asset::with('assetstatus')->with('assignedTo');
|
||||
|
||||
if ($request->input('deleted', 'false') === 'true') {
|
||||
$assets = $assets->withTrashed();
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
$assets = $assets->where('serial', $serial)->get();
|
||||
if ($assets) {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON with information about an asset for detail view.
|
||||
*
|
||||
@@ -343,17 +409,17 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function show($id)
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()
|
||||
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->findOrFail($id)) {
|
||||
$this->authorize('view', $asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request->input('components') );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public function licenses($id)
|
||||
public function licenses(Request $request, $id)
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', License::class);
|
||||
@@ -424,11 +490,11 @@ class AssetsController extends Controller
|
||||
* Accepts a POST request to create a new asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
|
||||
$this->authorize('create', Asset::class);
|
||||
@@ -449,7 +515,7 @@ class AssetsController extends Controller
|
||||
$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_cost = Helper::ParseCurrency($request->get('purchase_cost')); // this is the API's store method, so I don't know that I want to do this? Confusing. FIXME (or not?!)
|
||||
$asset->purchase_date = $request->get('purchase_date', null);
|
||||
$asset->assigned_to = $request->get('assigned_to', null);
|
||||
$asset->supplier_id = $request->get('supplier_id', 0);
|
||||
@@ -457,21 +523,15 @@ class AssetsController extends Controller
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id', null);
|
||||
$asset->location_id = $request->get('rtd_location_id', null);
|
||||
|
||||
if ($request->has('image_source') && $request->input('image_source') != "") {
|
||||
$saved_image_path = Helper::processUploadedImage(
|
||||
$request->input('image_source'), 'uploads/assets/'
|
||||
);
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
if (!$saved_image_path) {
|
||||
return response()->json(Helper::formatStandardApiResponse(
|
||||
'error',
|
||||
null,
|
||||
trans('admin/hardware/message.create.error')
|
||||
), 200);
|
||||
}
|
||||
|
||||
$asset->image = $saved_image_path;
|
||||
}
|
||||
$asset = $request->handleImages($asset);
|
||||
|
||||
// Update custom fields in the database.
|
||||
// Validation for these fields is handled through the AssetRequest form request
|
||||
@@ -539,11 +599,11 @@ class AssetsController extends Controller
|
||||
* Accepts a POST request to update an asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
@@ -557,32 +617,19 @@ class AssetsController extends Controller
|
||||
($request->filled('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
|
||||
if ($request->filled('image_source')) {
|
||||
if ($request->input('image_source') == "") {
|
||||
($request->filled('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
$asset->image = null;
|
||||
} else {
|
||||
$saved_image_path = Helper::processUploadedImage(
|
||||
$request->input('image_source'), 'uploads/assets/'
|
||||
);
|
||||
|
||||
if (!$saved_image_path) {
|
||||
return response()->json(Helper::formatStandardApiResponse(
|
||||
'error',
|
||||
null,
|
||||
trans('admin/hardware/message.update.error')
|
||||
), 200);
|
||||
}
|
||||
|
||||
$asset->image = $saved_image_path;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
|
||||
// Update custom fields
|
||||
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
@@ -657,6 +704,39 @@ class AssetsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Restore a soft-deleted asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v5.1.18]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function restore($assetId = null)
|
||||
{
|
||||
// Get asset information
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
$this->authorize('delete', $asset);
|
||||
if (isset($asset->id)) {
|
||||
// Restore the asset
|
||||
Asset::withTrashed()->where('id', $assetId)->restore();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Asset::class;
|
||||
$logaction->item_id = $asset->id;
|
||||
$logaction->created_at = date("Y-m-d H:i:s");
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restored');
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.restore.success')));
|
||||
|
||||
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -780,12 +860,18 @@ class AssetsController extends Controller
|
||||
$asset->status_id = $request->input('status_id');
|
||||
}
|
||||
|
||||
$checkin_at = null;
|
||||
if ($request->filled('checkin_at')) {
|
||||
$checkin_at = $request->input('checkin_at');
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
$asset->logCheckin($target, e($request->input('note')));
|
||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at));
|
||||
|
||||
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')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
}
|
||||
|
||||
|
||||
@@ -845,7 +931,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found'));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.e($request->input('asset_tag')).' not found'));
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Http\Transformers\CategoriesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class CategoriesController extends Controller
|
||||
@@ -54,14 +55,16 @@ class CategoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Category::class);
|
||||
$category = new Category;
|
||||
$category->fill($request->all());
|
||||
$category->category_type = strtolower($request->input('category_type'));
|
||||
$category = $request->handleImages($category);
|
||||
|
||||
if ($category->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.create.success')));
|
||||
@@ -92,15 +95,17 @@ class CategoriesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
$category->fill($request->all());
|
||||
$category->category_type = strtolower($request->input('category_type'));
|
||||
$category = $request->handleImages($category);
|
||||
|
||||
if ($category->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $category, trans('admin/categories/message.update.success')));
|
||||
@@ -143,7 +148,7 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function selectlist(Request $request, $category_type = 'asset')
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$categories = Category::select([
|
||||
'id',
|
||||
'name',
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Http\Transformers\CompaniesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class CompaniesController extends Controller
|
||||
@@ -65,15 +66,16 @@ class CompaniesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
$company = new Company;
|
||||
$company->fill($request->all());
|
||||
|
||||
$company = $request->handleImages($company);
|
||||
|
||||
if ($company->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new CompaniesTransformer)->transformCompany($company), trans('admin/companies/message.create.success')));
|
||||
}
|
||||
@@ -104,15 +106,16 @@ class CompaniesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$company->fill($request->all());
|
||||
$company = $request->handleImages($company);
|
||||
|
||||
if ($company->save()) {
|
||||
return response()
|
||||
@@ -156,7 +159,7 @@ class CompaniesController extends Controller
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$companies = Company::select([
|
||||
'companies.id',
|
||||
'companies.name',
|
||||
|
||||
@@ -8,6 +8,10 @@ use App\Http\Transformers\ComponentsTransformer;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use App\Events\ComponentCheckedIn;
|
||||
use App\Models\Asset;
|
||||
|
||||
class ComponentsController extends Controller
|
||||
{
|
||||
@@ -22,8 +26,25 @@ class ComponentsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
|
||||
// Relations will be handled in query scopes a little further down.
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'min_amt',
|
||||
'order_number',
|
||||
'serial',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'qty',
|
||||
'image',
|
||||
];
|
||||
|
||||
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')
|
||||
->with('company', 'location', 'category'));
|
||||
->with('company', 'location', 'category', 'assets'));
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$components = $components->TextSearch($request->input('search'));
|
||||
@@ -48,11 +69,12 @@ class ComponentsController extends Controller
|
||||
// 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';
|
||||
$sort_override = $request->input('sort');
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
|
||||
|
||||
switch ($sort) {
|
||||
switch ($sort_override) {
|
||||
case 'category':
|
||||
$components = $components->OrderCategory($order);
|
||||
break;
|
||||
@@ -63,7 +85,7 @@ class ComponentsController extends Controller
|
||||
$components = $components->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$components = $components->orderBy($sort, $order);
|
||||
$components = $components->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,14 +100,15 @@ class ComponentsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Component::class);
|
||||
$component = new Component;
|
||||
$component->fill($request->all());
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
if ($component->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.create.success')));
|
||||
@@ -116,15 +139,17 @@ class ComponentsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
$component->fill($request->all());
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
|
||||
if ($component->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $component, trans('admin/components/message.update.success')));
|
||||
@@ -172,4 +197,119 @@ class ComponentsController extends Controller
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new ComponentsTransformer)->transformCheckedoutComponents($assets, $total);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate and checkout the component.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* t
|
||||
* @since [v5.1.8]
|
||||
* @param Request $request
|
||||
* @param int $componentId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function checkout(Request $request, $componentId)
|
||||
{
|
||||
// Check if the component exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $component);
|
||||
|
||||
|
||||
if ($component->numRemaining() >= $request->get('assigned_qty')) {
|
||||
|
||||
if (!$asset = Asset::find($request->input('assigned_to'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')));
|
||||
}
|
||||
|
||||
// Update the accessory data
|
||||
$component->assigned_to = $request->input('assigned_to');
|
||||
|
||||
$component->assets()->attach($component->id, [
|
||||
'component_id' => $component->id,
|
||||
'created_at' => \Carbon::now(),
|
||||
'assigned_qty' => $request->get('assigned_qty', 1),
|
||||
'user_id' => \Auth::id(),
|
||||
'asset_id' => $request->get('assigned_to')
|
||||
]);
|
||||
|
||||
$component->logCheckout($request->input('note'), $asset);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Not enough components remaining: '.$component->numRemaining().' remaining, '.$request->get('assigned_qty').' requested.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and store checkin data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.1.8]
|
||||
* @param Request $request
|
||||
* @param $component_asset_id
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function checkin(Request $request, $component_asset_id)
|
||||
{
|
||||
if ($component_assets = \DB::table('components_assets')->find($component_asset_id)) {
|
||||
|
||||
if (is_null($component = Component::find($component_assets->component_id))) {
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.not_found')));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $component);
|
||||
|
||||
$max_to_checkin = $component_assets->assigned_qty;
|
||||
|
||||
if ($max_to_checkin > 1) {
|
||||
|
||||
$validator = \Validator::make($request->all(), [
|
||||
"checkin_qty" => "required|numeric|between:1,$max_to_checkin"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Checkin quantity must be between 1 and '.$max_to_checkin));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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', 1));
|
||||
|
||||
// We have to modify the record to reflect the new qty that's
|
||||
// actually checked out.
|
||||
$component_assets->assigned_qty = $qty_remaining_in_checkout;
|
||||
|
||||
\Log::debug($component_asset_id.' - '.$qty_remaining_in_checkout.' remaining in record '.$component_assets->id);
|
||||
|
||||
\DB::table('components_assets')->where('id',
|
||||
$component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
$asset = Asset::find($component_assets->asset_id);
|
||||
|
||||
event(new CheckoutableCheckedIn($component, $asset, \Auth::user(), $request->input('note'), \Carbon::now()));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkin.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'No matching checkouts for that component join record'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Company;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
class ConsumablesController extends Controller
|
||||
{
|
||||
@@ -24,6 +25,26 @@ class ConsumablesController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('index', Consumable::class);
|
||||
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself, no relations
|
||||
// Relations will be handled in query scopes a little further down.
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'order_number',
|
||||
'min_amt',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'company',
|
||||
'category',
|
||||
'model_number',
|
||||
'item_no',
|
||||
'qty',
|
||||
'image',
|
||||
];
|
||||
|
||||
|
||||
$consumables = Company::scopeCompanyables(
|
||||
Consumable::select('consumables.*')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
||||
@@ -41,10 +62,18 @@ class ConsumablesController extends Controller
|
||||
$consumables->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('model_number')) {
|
||||
$consumables->where('model_number','=',$request->input('model_number'));
|
||||
}
|
||||
|
||||
if ($request->filled('manufacturer_id')) {
|
||||
$consumables->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
$consumables->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
@@ -53,12 +82,14 @@ class ConsumablesController extends Controller
|
||||
// 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';
|
||||
|
||||
$sort_override = $request->input('sort');
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
|
||||
|
||||
|
||||
switch ($sort) {
|
||||
switch ($sort_override) {
|
||||
case 'category':
|
||||
$consumables = $consumables->OrderCategory($order);
|
||||
break;
|
||||
@@ -72,7 +103,7 @@ class ConsumablesController extends Controller
|
||||
$consumables = $consumables->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$consumables = $consumables->orderBy($sort, $order);
|
||||
$consumables = $consumables->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -90,14 +121,15 @@ class ConsumablesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
$consumable = new Consumable;
|
||||
$consumable->fill($request->all());
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.create.success')));
|
||||
@@ -125,16 +157,17 @@ class ConsumablesController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
$consumable->fill($request->all());
|
||||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
if ($consumable->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $consumable, trans('admin/consumables/message.update.success')));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Department;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DepartmentsController extends Controller
|
||||
@@ -73,14 +74,16 @@ class DepartmentsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$department = new Department;
|
||||
$department->fill($request->all());
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
$department->user_id = Auth::user()->id;
|
||||
$department->manager_id = ($request->filled('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
@@ -111,15 +114,16 @@ class DepartmentsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Department::class);
|
||||
$department = Department::findOrFail($id);
|
||||
$department->fill($request->all());
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.update.success')));
|
||||
@@ -164,6 +168,7 @@ class DepartmentsController extends Controller
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$departments = Department::select([
|
||||
'id',
|
||||
'name',
|
||||
|
||||
@@ -20,9 +20,9 @@ class DepreciationsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = ['id','name','months','created_at'];
|
||||
$allowed_columns = ['id','name','months','depreciation_min','created_at'];
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');
|
||||
$depreciations = Depreciation::select('id','name','months','depreciation_min','user_id','created_at','updated_at');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
|
||||
@@ -17,6 +17,8 @@ class LicenseSeatsController extends Controller
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request, $licenseId)
|
||||
@@ -53,7 +55,8 @@ class LicenseSeatsController extends Controller
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $licenseId
|
||||
* @param int $seatId
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($licenseId, $seatId)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Location;
|
||||
@@ -86,14 +87,15 @@ class LocationsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$location = new Location;
|
||||
$location->fill($request->all());
|
||||
$location = $request->handleImages($location);
|
||||
|
||||
if ($location->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new LocationsTransformer)->transformLocation($location), trans('admin/locations/message.create.success')));
|
||||
@@ -141,17 +143,17 @@ class LocationsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
|
||||
$location->fill($request->all());
|
||||
|
||||
$location = $request->handleImages($location);
|
||||
|
||||
if ($location->isValid()) {
|
||||
|
||||
@@ -221,6 +223,8 @@ class LocationsController extends Controller
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
|
||||
$locations = Location::select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Http\Transformers\ManufacturersTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ManufacturersController extends Controller
|
||||
@@ -59,14 +60,15 @@ class ManufacturersController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = new Manufacturer;
|
||||
$manufacturer->fill($request->all());
|
||||
$manufacturer = $request->handleImages($manufacturer);
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.create.success')));
|
||||
@@ -96,15 +98,16 @@ class ManufacturersController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer->fill($request->all());
|
||||
$manufacturer = $request->handleImages($manufacturer);
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $manufacturer, trans('admin/manufacturers/message.update.success')));
|
||||
@@ -152,6 +155,7 @@ class ManufacturersController extends Controller
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$manufacturers = Manufacturer::select([
|
||||
'id',
|
||||
'name',
|
||||
|
||||
@@ -31,8 +31,8 @@ class PredefinedKitsController extends Controller
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
|
||||
$order = $request->input('order') === 'desc' ? 'desc' : 'asc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'name';
|
||||
$kits->orderBy($sort, $order);
|
||||
|
||||
$total = $kits->count();
|
||||
|
||||
@@ -17,6 +17,7 @@ use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Models\Ldap; // forward-port of v4 LDAP model for Sync
|
||||
use App\Http\Requests\SlackSettingsRequest;
|
||||
|
||||
|
||||
class SettingsController extends Controller
|
||||
@@ -165,31 +166,41 @@ class SettingsController extends Controller
|
||||
public function slacktest(Request $request)
|
||||
{
|
||||
|
||||
$slack = new Client([
|
||||
'base_url' => e($request->input('slack_endpoint')),
|
||||
'defaults' => [
|
||||
'exceptions' => false
|
||||
]
|
||||
$validator = Validator::make($request->all(), [
|
||||
'slack_endpoint' => 'url|required_with:slack_channel|starts_with:https://hooks.slack.com/|nullable',
|
||||
'slack_channel' => 'required_with:slack_endpoint|starts_with:#|nullable',
|
||||
]);
|
||||
|
||||
|
||||
$payload = json_encode(
|
||||
[
|
||||
'channel' => e($request->input('slack_channel')),
|
||||
'text' => trans('general.slack_test_msg'),
|
||||
'username' => e($request->input('slack_botname')),
|
||||
'icon_emoji' => ':heart:'
|
||||
]);
|
||||
|
||||
try {
|
||||
$slack->post($request->input('slack_endpoint'),['body' => $payload]);
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['message' => 'Oops! Please check the channel name and webhook endpoint URL. Slack responded with: '.$e->getMessage()], 400);
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['message' => 'Validation failed', 'errors' => $validator->errors()], 422);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Something went wrong :( '], 400);
|
||||
// If validation passes, continue to the curl request
|
||||
$slack = new Client([
|
||||
'base_url' => e($request->input('slack_endpoint')),
|
||||
'defaults' => [
|
||||
'exceptions' => false,
|
||||
],
|
||||
]);
|
||||
|
||||
$payload = json_encode(
|
||||
[
|
||||
'channel' => e($request->input('slack_channel')),
|
||||
'text' => trans('general.slack_test_msg'),
|
||||
'username' => e($request->input('slack_botname')),
|
||||
'icon_emoji' => ':heart:',
|
||||
]);
|
||||
|
||||
try {
|
||||
$slack->post($request->input('slack_endpoint'), ['body' => $payload]);
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['message' => 'Please check the channel name and webhook endpoint URL ('.$request->input('slack_endpoint').'). Slack responded with: '.$e->getMessage()], 400);
|
||||
}
|
||||
|
||||
//}
|
||||
return response()->json(['message' => 'Something went wrong :( '], 400);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class StatuslabelsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$allowed_columns = ['id','name','created_at', 'assets_count','color','default_label'];
|
||||
$allowed_columns = ['id','name','created_at', 'assets_count','color', 'notes','default_label'];
|
||||
|
||||
$statuslabels = Statuslabel::withCount('assets as assets_count');
|
||||
|
||||
@@ -71,6 +71,10 @@ class StatuslabelsController extends Controller
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
$statuslabel->color = $request->input('color');
|
||||
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
|
||||
$statuslabel->default_label = $request->input('default_label', 0);
|
||||
|
||||
|
||||
if ($statuslabel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.create.success')));
|
||||
@@ -111,9 +115,7 @@ class StatuslabelsController extends Controller
|
||||
|
||||
$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());
|
||||
|
||||
@@ -121,6 +123,9 @@ class StatuslabelsController extends Controller
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
$statuslabel->color = $request->input('color');
|
||||
$statuslabel->show_in_nav = $request->input('show_in_nav');
|
||||
$statuslabel->default_label = $request->input('default_label');
|
||||
|
||||
if ($statuslabel->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $statuslabel, trans('admin/statuslabels/message.update.success')));
|
||||
@@ -167,10 +172,7 @@ class StatuslabelsController extends Controller
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
$statuslabels = Statuslabel::with('assets')
|
||||
->groupBy('id')
|
||||
->withCount('assets as assets_count')
|
||||
->get();
|
||||
$statuslabels = Statuslabel::withCount('assets')->get();
|
||||
|
||||
$labels=[];
|
||||
$points=[];
|
||||
@@ -179,7 +181,6 @@ class StatuslabelsController extends Controller
|
||||
|
||||
foreach ($statuslabels as $statuslabel) {
|
||||
if ($statuslabel->assets_count > 0) {
|
||||
|
||||
$labels[]=$statuslabel->name. ' ('.number_format($statuslabel->assets_count).')';
|
||||
$points[]=$statuslabel->assets_count;
|
||||
|
||||
@@ -187,8 +188,8 @@ class StatuslabelsController extends Controller
|
||||
$colors_array[] = $statuslabel->color;
|
||||
} else {
|
||||
$colors_array[] = Helper::defaultChartColors($default_color_count);
|
||||
$default_color_count++;
|
||||
}
|
||||
$default_color_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\SuppliersTransformer;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SuppliersController extends Controller
|
||||
@@ -55,14 +56,15 @@ class SuppliersController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Supplier::class);
|
||||
$supplier = new Supplier;
|
||||
$supplier->fill($request->all());
|
||||
$supplier = $request->handleImages($supplier);
|
||||
|
||||
if ($supplier->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.create.success')));
|
||||
@@ -92,15 +94,16 @@ class SuppliersController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
$this->authorize('update', Supplier::class);
|
||||
$supplier = Supplier::findOrFail($id);
|
||||
$supplier->fill($request->all());
|
||||
$supplier = $request->handleImages($supplier);
|
||||
|
||||
if ($supplier->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $supplier, trans('admin/suppliers/message.update.success')));
|
||||
@@ -152,6 +155,8 @@ class SuppliersController extends Controller
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
|
||||
$suppliers = Supplier::select([
|
||||
'id',
|
||||
'name',
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\License;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UsersController extends Controller
|
||||
@@ -74,6 +75,10 @@ class UsersController extends Controller
|
||||
$users = $users->withTrashed();
|
||||
}
|
||||
|
||||
if ($request->filled('activated')) {
|
||||
$users = $users->where('users.activated', '=', $request->input('activated'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$users = $users->where('users.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
@@ -90,6 +95,30 @@ class UsersController extends Controller
|
||||
$users = $users->where('users.username', '=', $request->input('username'));
|
||||
}
|
||||
|
||||
if ($request->filled('first_name')) {
|
||||
$users = $users->where('users.first_name', '=', $request->input('first_name'));
|
||||
}
|
||||
|
||||
if ($request->filled('last_name')) {
|
||||
$users = $users->where('users.last_name', '=', $request->input('last_name'));
|
||||
}
|
||||
|
||||
if ($request->filled('employee_num')) {
|
||||
$users = $users->where('users.employee_num', '=', $request->input('employee_num'));
|
||||
}
|
||||
|
||||
if ($request->filled('state')) {
|
||||
$users = $users->where('users.state', '=', $request->input('state'));
|
||||
}
|
||||
|
||||
if ($request->filled('country')) {
|
||||
$users = $users->where('users.country', '=', $request->input('country'));
|
||||
}
|
||||
|
||||
if ($request->filled('zip')) {
|
||||
$users = $users->where('users.zip', '=', $request->input('zip'));
|
||||
}
|
||||
|
||||
if ($request->filled('group_id')) {
|
||||
$users = $users->ByGroup($request->get('group_id'));
|
||||
}
|
||||
@@ -98,6 +127,10 @@ class UsersController extends Controller
|
||||
$users = $users->where('users.department_id','=',$request->input('department_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('manager_id')) {
|
||||
$users = $users->where('users.manager_id','=',$request->input('manager_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
@@ -237,7 +270,8 @@ class UsersController extends Controller
|
||||
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
$user->password = bcrypt($request->get('password', $tmp_pass));
|
||||
|
||||
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->filled('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
@@ -280,9 +314,15 @@ class UsersController extends Controller
|
||||
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
// This is a janky hack to prevent people from changing admin demo user data on the public demo.
|
||||
// The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
|
||||
// Thanks, jerks. You are why we can't have nice things. - snipe
|
||||
/**
|
||||
* This is a janky hack to prevent people from changing admin demo user data on the public demo.
|
||||
*
|
||||
* The $ids 1 and 2 are special since they are seeded as superadmins in the demo seeder.
|
||||
*
|
||||
* Thanks, jerks. You are why we can't have nice things. - snipe
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
if ((($id == 1) || ($id == 2)) && (config('app.lock_passwords'))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Permission denied. You cannot update user information via API on the demo.'));
|
||||
@@ -290,7 +330,7 @@ class UsersController extends Controller
|
||||
|
||||
|
||||
$user->fill($request->all());
|
||||
|
||||
|
||||
if ($user->id == $request->input('manager_id')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
|
||||
}
|
||||
@@ -320,6 +360,9 @@ class UsersController extends Controller
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
if ($user->save()) {
|
||||
|
||||
// Sync group memberships:
|
||||
@@ -397,12 +440,12 @@ class UsersController extends Controller
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function assets($id)
|
||||
public function assets(Request $request, $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 (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,7 +100,7 @@ class AssetMaintenancesController extends Controller
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
$asset = Asset::find($request->input('asset_id'));
|
||||
|
||||
@@ -209,10 +209,10 @@ class AssetMaintenancesController extends Controller
|
||||
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'));
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = Helper::ParseCurrency($request->input('cost'));
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
|
||||
@@ -155,7 +155,6 @@ class AssetModelsController extends Controller
|
||||
$model->requestable = $request->input('requestable', '0');
|
||||
|
||||
|
||||
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if ($request->input('custom_fieldset')=='') {
|
||||
@@ -168,7 +167,6 @@ class AssetModelsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($model->save()) {
|
||||
return redirect()->route("models.index")->with('success', trans('admin/models/message.update.success'));
|
||||
}
|
||||
@@ -269,6 +267,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function getClone($modelId = null)
|
||||
{
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model_to_clone = AssetModel::find($modelId))) {
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
@@ -462,7 +461,9 @@ class AssetModelsController extends Controller
|
||||
private function assignCustomFieldsDefaultValues(AssetModel $model, array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $customFieldId => $defaultValue) {
|
||||
if ($defaultValue) {
|
||||
if(is_array($defaultValue)){
|
||||
$model->defaultValues()->attach($customFieldId, ['default_value' => implode(', ', $defaultValue)]);
|
||||
}elseif ($defaultValue) {
|
||||
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers\Assets;
|
||||
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
@@ -10,6 +9,7 @@ use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\StorageHelper;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
@@ -36,9 +36,29 @@ class AssetFilesController extends Controller
|
||||
if (!Storage::exists('private_uploads/assets')) Storage::makeDirectory('private_uploads/assets', 775);
|
||||
|
||||
foreach ($request->file('file') as $file) {
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = 'hardware-'.$asset->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
Storage::put('private_uploads/assets/'.$file_name, file_get_contents($file));
|
||||
|
||||
// Check for SVG and sanitize it
|
||||
if ($extension=='svg') {
|
||||
\Log::debug('This is an SVG');
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put('private_uploads/assets/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Upload no workie :( ');
|
||||
\Log::debug($e);
|
||||
}
|
||||
} else {
|
||||
Storage::put('private_uploads/assets/'.$file_name, file_get_contents($file));
|
||||
}
|
||||
|
||||
|
||||
$asset->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
@@ -110,20 +130,20 @@ class AssetFilesController extends Controller
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
$this->authorize('update', $asset);
|
||||
$rel_path = 'storage/private_uploads/assets';
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
if (file_exists(base_path().'/'.$rel_path.'/'.$log->filename)) {
|
||||
Storage::disk('public')->delete($rel_path.'/'.$log->filename);
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class AssetsController extends Controller
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = request('status_id', 0);
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
|
||||
$asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost'));
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', 0);
|
||||
@@ -196,7 +196,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', e($request->get('name')), $location);
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
|
||||
}
|
||||
|
||||
$success = true;
|
||||
@@ -302,7 +302,7 @@ class AssetsController extends Controller
|
||||
|
||||
$asset->status_id = $request->input('status_id', null);
|
||||
$asset->warranty_months = $request->input('warranty_months', null);
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->input('purchase_cost', null));
|
||||
$asset->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->supplier_id = $request->input('supplier_id', null);
|
||||
$asset->expected_checkin = $request->input('expected_checkin', null);
|
||||
@@ -813,7 +813,7 @@ class AssetsController extends Controller
|
||||
$asset->unsetEventDispatcher();
|
||||
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
$asset->last_audit_date = date('Y-m-d h:i:s');
|
||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||
|
||||
// Check to see if they checked the box to update the physical location,
|
||||
// not just note it in the audit notes
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Assets;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -32,7 +33,8 @@ class BulkAssetsController extends Controller
|
||||
return redirect()->back()->with('error', 'No assets selected');
|
||||
}
|
||||
|
||||
$asset_ids = array_keys($request->input('ids'));
|
||||
|
||||
$asset_ids = array_values(array_unique($request->input('ids')));
|
||||
|
||||
if ($request->filled('bulk_actions')) {
|
||||
switch($request->input('bulk_actions')) {
|
||||
@@ -50,7 +52,7 @@ class BulkAssetsController extends Controller
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
case 'edit':
|
||||
return view('hardware/bulk')
|
||||
->with('assets', request('ids'))
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList());
|
||||
}
|
||||
}
|
||||
@@ -90,6 +92,7 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('model_id'))
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
|
||||
$this->update_array = [];
|
||||
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
@@ -102,7 +105,7 @@ class BulkAssetsController extends Controller
|
||||
->conditionallyAddItem('warranty_months');
|
||||
|
||||
if ($request->filled('purchase_cost')) {
|
||||
$this->update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost'));
|
||||
$this->update_array['purchase_cost'] = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
@@ -119,6 +122,24 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$changed = [];
|
||||
$asset = Asset::where('id' ,$assetId)->get();
|
||||
|
||||
foreach ($this->update_array as $key => $value) {
|
||||
if ($this->update_array[$key] != $asset->toArray()[0][$key]) {
|
||||
$changed[$key]['old'] = $asset->toArray()[0][$key];
|
||||
$changed[$key]['new'] = $this->update_array[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $assetId;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
$logAction->logaction('update');
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $assetId)
|
||||
->update($this->update_array);
|
||||
|
||||
@@ -29,6 +29,7 @@ class ForgotPasswordController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
$this->middleware('throttle:5,1', ['except' => 'showLinkRequestForm']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +62,7 @@ class ForgotPasswordController extends Controller
|
||||
$request->validate([
|
||||
'username' => ['required', 'max:255'],
|
||||
]);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -71,13 +72,21 @@ class ForgotPasswordController extends Controller
|
||||
* 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('username'),
|
||||
['activated' => '1'],
|
||||
['ldap_import' => '0']
|
||||
)
|
||||
);
|
||||
|
||||
$response = null;
|
||||
|
||||
try {
|
||||
$response = $this->broker()->sendResetLink(
|
||||
array_merge(
|
||||
$request->only('username'),
|
||||
['activated' => '1'],
|
||||
['ldap_import' => '0']
|
||||
)
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
\Log::info('Password reset attempt: User '.$request->input('username').'failed with exception: '.$e );
|
||||
}
|
||||
|
||||
|
||||
if ($response === \Password::RESET_LINK_SENT) {
|
||||
\Log::info('Password reset attempt: User '.$request->input('username').' WAS found, password reset sent');
|
||||
|
||||
@@ -108,7 +108,7 @@ class LoginController extends Controller
|
||||
Log::debug("Attempting to log user in by SAML authentication.");
|
||||
$user = $saml->samlLogin($samlData);
|
||||
if(!is_null($user)) {
|
||||
Auth::login($user, true);
|
||||
Auth::login($user);
|
||||
} else {
|
||||
$username = $saml->getUsername();
|
||||
\Log::warning("SAML user '$username' could not be found in database.");
|
||||
@@ -183,7 +183,7 @@ class LoginController extends Controller
|
||||
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);
|
||||
if(!is_null($user)) Auth::login($user, $request->input('remember'));
|
||||
} catch(Exception $e) {
|
||||
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
|
||||
}
|
||||
@@ -223,7 +223,7 @@ class LoginController extends Controller
|
||||
try {
|
||||
LOG::debug("Attempting to log user in by LDAP authentication.");
|
||||
$user = $this->loginViaLdap($request);
|
||||
Auth::login($user, true);
|
||||
Auth::login($user, $request->input('remember'));
|
||||
|
||||
// If the user was unable to login via LDAP, log the error and let them fall through to
|
||||
// local authentication.
|
||||
|
||||
@@ -32,6 +32,7 @@ class BulkAssetModelsController extends Controller
|
||||
|
||||
// If deleting....
|
||||
if ($request->input('bulk_actions')=='delete') {
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
$valid_count = 0;
|
||||
foreach ($models as $model) {
|
||||
if ($model->assets_count == 0) {
|
||||
@@ -42,7 +43,7 @@ class BulkAssetModelsController extends Controller
|
||||
|
||||
// Otherwise display the bulk edit screen
|
||||
}
|
||||
|
||||
$this->authorize('update', AssetModel::class);
|
||||
$nochange = ['NC' => 'No Change'];
|
||||
return view('models/bulk-edit', compact('models'))
|
||||
->with('fieldset_list', $nochange + Helper::customFieldsetList())
|
||||
@@ -63,7 +64,8 @@ class BulkAssetModelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('update', AssetModel::class);
|
||||
|
||||
$models_raw_array = $request->input('ids');
|
||||
$update_array = array();
|
||||
|
||||
@@ -103,6 +105,8 @@ class BulkAssetModelsController extends Controller
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
|
||||
$models_raw_array = $request->input('ids');
|
||||
|
||||
if ((is_array($models_raw_array)) && (count($models_raw_array) > 0)) {
|
||||
|
||||
@@ -5,6 +5,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Helpers\Helper;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -74,7 +75,7 @@ class ComponentsController extends Controller
|
||||
$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->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
|
||||
$component->qty = $request->input('qty');
|
||||
$component->user_id = Auth::id();
|
||||
|
||||
@@ -144,7 +145,7 @@ class ComponentsController extends Controller
|
||||
$component->min_amt = $request->input('min_amt');
|
||||
$component->serial = $request->input('serial');
|
||||
$component->purchase_date = $request->input('purchase_date');
|
||||
$component->purchase_cost = request('purchase_cost');
|
||||
$component->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$component->qty = $request->input('qty');
|
||||
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
@@ -75,7 +75,7 @@ class ConsumablesController extends Controller
|
||||
$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->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$consumable->qty = $request->input('qty');
|
||||
$consumable->user_id = Auth::id();
|
||||
|
||||
@@ -141,7 +141,7 @@ class ConsumablesController extends Controller
|
||||
$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->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$consumable->qty = Helper::ParseFloat($request->input('qty'));
|
||||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
@@ -92,7 +92,7 @@ class CustomFieldsController extends Controller
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$field = new CustomField([
|
||||
"name" => $request->get("name"),
|
||||
"name" => trim($request->get("name")),
|
||||
"element" => $request->get("element"),
|
||||
"help_text" => $request->get("help_text"),
|
||||
"field_values" => $request->get("field_values"),
|
||||
@@ -133,12 +133,23 @@ class CustomFieldsController extends Controller
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
if ($field->fieldset()->detach($fieldset_id)) {
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
|
||||
->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
// Check that the field exists - this is mostly related to the demo, where we
|
||||
// rewrite the data every x minutes, so it's possible someone might be disassociating
|
||||
// a field from a fieldset just as we're wiping the database
|
||||
if (($field) && ($fieldset_id)) {
|
||||
|
||||
if ($field->fieldset()->detach($fieldset_id)) {
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
|
||||
->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
} else {
|
||||
return redirect()->back()->withErrors(['message' => "Field is in use and cannot be deleted."]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
|
||||
return redirect()->back()->withErrors(['message' => "Error deleting field from fieldset"]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,20 +189,25 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$field = CustomField::find($id);
|
||||
if ($field = CustomField::find($id)) {
|
||||
|
||||
$this->authorize('update', $field);
|
||||
$this->authorize('update', $field);
|
||||
|
||||
$customFormat = '';
|
||||
if((stripos($field->format, 'regex') === 0) && ($field->format !== CustomField::PREDEFINED_FORMATS['MAC'])) {
|
||||
$customFormat = $field->format;
|
||||
}
|
||||
$customFormat = '';
|
||||
if((stripos($field->format, 'regex') === 0) && ($field->format !== CustomField::PREDEFINED_FORMATS['MAC'])) {
|
||||
$customFormat = $field->format;
|
||||
}
|
||||
|
||||
return view("custom_fields.fields.edit",[
|
||||
'field' => $field,
|
||||
'customFormat' => $customFormat,
|
||||
'predefinedFormats' => Helper::predefined_formats()
|
||||
]);
|
||||
return view("custom_fields.fields.edit",[
|
||||
'field' => $field,
|
||||
'customFormat' => $customFormat,
|
||||
'predefinedFormats' => Helper::predefined_formats()
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route("fields.index")
|
||||
->with("error", trans('admin/custom_fields/message.field.invalid'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +228,7 @@ class CustomFieldsController extends Controller
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
$field->name = e($request->get("name"));
|
||||
$field->name = trim(e($request->get("name")));
|
||||
$field->element = e($request->get("element"));
|
||||
$field->field_values = e($request->get("field_values"));
|
||||
$field->user_id = Auth::id();
|
||||
|
||||
@@ -23,6 +23,12 @@ use Redirect;
|
||||
class CustomFieldsetsController extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
return redirect()->route("fields.index")
|
||||
->with("error", trans('admin/custom_fields/message.fieldset.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores a new custom field.
|
||||
*
|
||||
|
||||
@@ -69,6 +69,7 @@ class DepreciationsController extends Controller
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
$depreciation->depreciation_min= $request->input('depreciation_min');
|
||||
$depreciation->user_id = Auth::id();
|
||||
|
||||
// Was the asset created?
|
||||
@@ -125,8 +126,9 @@ class DepreciationsController extends Controller
|
||||
$this->authorize('update', $depreciation);
|
||||
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
$depreciation->depreciation_min = $request->input('depreciation_min');
|
||||
|
||||
// Was the asset created?
|
||||
if ($depreciation->save()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\User;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
@@ -80,7 +81,12 @@ class LicenseCheckinController extends Controller
|
||||
// Ooops.. something went wrong
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
|
||||
if($licenseSeat->assigned_to != null){
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
} else {
|
||||
$return_to = Asset::find($licenseSeat->asset_id);
|
||||
}
|
||||
|
||||
// Update the asset data
|
||||
$licenseSeat->assigned_to = null;
|
||||
@@ -88,7 +94,6 @@ class LicenseCheckinController extends Controller
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
|
||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, Auth::user(), $request->input('note')));
|
||||
|
||||
if ($backTo=='user') {
|
||||
|
||||
@@ -6,11 +6,11 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\License;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use App\Helpers\StorageHelper;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
class LicenseFilesController extends Controller
|
||||
{
|
||||
@@ -37,28 +37,39 @@ class LicenseFilesController extends Controller
|
||||
|
||||
if (!Storage::exists('private_uploads/licenses')) Storage::makeDirectory('private_uploads/licenses', 775);
|
||||
|
||||
$upload_success = false;
|
||||
foreach ($request->file('file') as $file) {
|
||||
|
||||
|
||||
$file_name = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$file->getClientOriginalExtension())).'.'.$file->getClientOriginalExtension();
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
|
||||
|
||||
$upload_success = $file->storeAs('private_uploads/licenses', $file_name);
|
||||
// $upload_success = $file->storeAs('private_uploads/licenses/'.$file_name, $file);
|
||||
// Check for SVG and sanitize it
|
||||
if ($extension == 'svg') {
|
||||
\Log::debug('This is an SVG');
|
||||
\Log::debug($file_name);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put('private_uploads/licenses/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Upload no workie :( ');
|
||||
\Log::debug($e);
|
||||
}
|
||||
|
||||
} else {
|
||||
Storage::put('private_uploads/licenses/'.$file_name, file_get_contents($file));
|
||||
}
|
||||
|
||||
//Log the upload to the log
|
||||
$license->logUpload($file_name, e($request->input('notes')));
|
||||
}
|
||||
|
||||
// This being called from a modal seems to confuse redirect()->back()
|
||||
// It thinks we should go to the dashboard. As this is only used
|
||||
// from the modal at present, hardcode the redirect. Longterm
|
||||
// maybe we evaluate something else.
|
||||
if ($upload_success) {
|
||||
|
||||
return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
|
||||
}
|
||||
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.error'));
|
||||
|
||||
}
|
||||
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class LicensesController extends Controller
|
||||
$license->name = $request->input('name');
|
||||
$license->notes = $request->input('notes');
|
||||
$license->order_number = $request->input('order_number');
|
||||
$license->purchase_cost = $request->input('purchase_cost');
|
||||
$license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$license->purchase_date = $request->input('purchase_date');
|
||||
$license->purchase_order = $request->input('purchase_order');
|
||||
$license->purchase_order = $request->input('purchase_order');
|
||||
@@ -165,7 +165,7 @@ class LicensesController extends Controller
|
||||
$license->name = $request->input('name');
|
||||
$license->notes = $request->input('notes');
|
||||
$license->order_number = $request->input('order_number');
|
||||
$license->purchase_cost = $request->input('purchase_cost');
|
||||
$license->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$license->purchase_date = $request->input('purchase_date');
|
||||
$license->purchase_order = $request->input('purchase_order');
|
||||
$license->reassignable = $request->input('reassignable', 0);
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,15 +6,49 @@ use App\Helpers\Helper;
|
||||
|
||||
class ModalController extends Controller
|
||||
{
|
||||
function show($type, $itemId = null) {
|
||||
$view = view("modals.${type}");
|
||||
|
||||
if($type == "statuslabel") {
|
||||
$view->with('statuslabel_types', Helper::statusTypeList());
|
||||
/**
|
||||
* Load the modal views after confirming they are in the allowed_types array.
|
||||
* The allowed types away just prevents shithead skiddies from fuzzing the urls
|
||||
* with automated scripts and junking up the logs. - snipe
|
||||
*
|
||||
* @version v5.3.7-pre
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net]
|
||||
* @return View
|
||||
*/
|
||||
function show ($type, $itemId = null) {
|
||||
|
||||
// These values should correspond to a file in resources/views/modals/
|
||||
$allowed_types = [
|
||||
'category',
|
||||
'kit-model',
|
||||
'kit-license',
|
||||
'kit-consumable',
|
||||
'kit-accessory',
|
||||
'location',
|
||||
'manufacturer',
|
||||
'model',
|
||||
'statuslabel',
|
||||
'supplier',
|
||||
'upload-file',
|
||||
'user',
|
||||
];
|
||||
|
||||
|
||||
if (in_array($type, $allowed_types)) {
|
||||
$view = view("modals.${type}");
|
||||
|
||||
if ($type == "statuslabel") {
|
||||
$view->with('statuslabel_types', Helper::statusTypeList());
|
||||
}
|
||||
if (in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) {
|
||||
$view->with('kitId', $itemId);
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
if(in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) {
|
||||
$view->with('kitId', $itemId);
|
||||
}
|
||||
return $view;
|
||||
|
||||
abort(404,'Page not found');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ class ProfileController extends Controller
|
||||
* @return View
|
||||
*/
|
||||
public function api() {
|
||||
|
||||
// Make sure the self.api permission has been granted
|
||||
if (!Gate::allows('self.api')) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return view('account/api');
|
||||
}
|
||||
|
||||
|
||||
@@ -102,11 +102,7 @@ class ReportsController extends Controller
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$depreciations = Depreciation::get();
|
||||
// Grab all the assets
|
||||
$assets = Asset::with( 'assignedTo', 'assetstatus', 'defaultLoc', 'location', 'company', 'model.category', 'model.depreciation')
|
||||
->orderBy('created_at', 'DESC')->get();
|
||||
|
||||
return view('reports/depreciation', compact('assets'))->with('depreciations',$depreciations);
|
||||
return view('reports/depreciation')->with('depreciations',$depreciations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,6 +515,10 @@ class ReportsController extends Controller
|
||||
$header[] = trans('general.department');
|
||||
}
|
||||
|
||||
if ($request->filled('title')) {
|
||||
$header[] = trans('admin/users/table.title');
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$header[] = trans('general.status');
|
||||
}
|
||||
@@ -563,7 +563,7 @@ class ReportsController extends Controller
|
||||
|
||||
|
||||
foreach ($customfields as $customfield) {
|
||||
if (e($request->input($customfield->db_column_name())) == '1') {
|
||||
if ($request->input($customfield->db_column_name()) == '1') {
|
||||
$header[] = $customfield->name;
|
||||
}
|
||||
}
|
||||
@@ -641,7 +641,7 @@ class ReportsController extends Controller
|
||||
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
|
||||
}
|
||||
|
||||
$assets->orderBy('assets.created_at', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
|
||||
$assets->orderBy('assets.id', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
|
||||
|
||||
$executionTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
|
||||
\Log::debug('Walking results: '.$executionTime);
|
||||
@@ -758,7 +758,6 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('department')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = (($asset->assignedto) && ($asset->assignedto->department)) ? $asset->assignedto->department->name : '';
|
||||
@@ -767,6 +766,14 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('title')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->jobtitle : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ use Input;
|
||||
use Redirect;
|
||||
use Response;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Requests\SlackSettingsRequest;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Settings for
|
||||
@@ -667,25 +668,16 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function postSlack(Request $request)
|
||||
public function postSlack(SlackSettingsRequest $request)
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$validatedData = $request->validate([
|
||||
'slack_channel' => 'regex:/(?<!\w)#\w+/|required_with:slack_endpoint|nullable',
|
||||
]);
|
||||
|
||||
|
||||
if ($validatedData) {
|
||||
|
||||
$setting->slack_endpoint = $request->input('slack_endpoint');
|
||||
$setting->slack_channel = $request->input('slack_channel');
|
||||
$setting->slack_botname = $request->input('slack_botname');
|
||||
|
||||
}
|
||||
|
||||
$setting->slack_endpoint = $request->input('slack_endpoint');
|
||||
$setting->slack_channel = $request->input('slack_channel');
|
||||
$setting->slack_botname = $request->input('slack_botname');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
->with('success', trans('admin/settings/message.update.success'));
|
||||
@@ -947,10 +939,14 @@ class SettingsController extends Controller
|
||||
$setting->ldap_jobtitle = $request->input('ldap_jobtitle');
|
||||
$setting->ldap_country = $request->input('ldap_country');
|
||||
$setting->ldap_dept = $request->input('ldap_dept');
|
||||
$setting->ldap_client_tls_cert = $request->input('ldap_client_tls_cert');
|
||||
$setting->ldap_client_tls_key = $request->input('ldap_client_tls_key');
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
$setting->update_client_side_cert_files();
|
||||
return redirect()->route('settings.ldap.index')
|
||||
->with('success', trans('admin/settings/message.update.success'));
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ class SuppliersController extends Controller
|
||||
*/
|
||||
public function show($supplierId = null)
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$supplier = Supplier::find($supplierId);
|
||||
|
||||
if (isset($supplier->id)) {
|
||||
|
||||
@@ -34,7 +34,8 @@ class BulkUsersController extends Controller
|
||||
// Make sure there were users selected
|
||||
if (($request->filled('ids')) && (count($request->input('ids')) > 0)) {
|
||||
// Get the list of affected users
|
||||
$users = User::whereIn('id', array_keys(request('ids')))
|
||||
$user_raw_array = request('ids');
|
||||
$users = User::whereIn('id', $user_raw_array)
|
||||
->with('groups', 'assets', 'licenses', 'accessories')->get();
|
||||
|
||||
if ($request->input('bulk_actions') == 'edit') {
|
||||
|
||||
@@ -10,6 +10,8 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UserFilesController extends Controller
|
||||
{
|
||||
@@ -33,13 +35,36 @@ class UserFilesController extends Controller
|
||||
|
||||
$logActions = [];
|
||||
$files = $request->file('file');
|
||||
|
||||
if (is_null($files)){
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
|
||||
}
|
||||
foreach($files as $file) {
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'user-' . $user->id . '-' . str_random(8);
|
||||
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
|
||||
if (!$file->move($destinationPath, $filename)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.invalidfiles'));
|
||||
}
|
||||
$file_name = 'user-'.$user->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
|
||||
|
||||
// Check for SVG and sanitize it
|
||||
if ($extension == 'svg') {
|
||||
\Log::debug('This is an SVG');
|
||||
\Log::debug($file_name);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put('private_uploads/users/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Upload no workie :( ');
|
||||
\Log::debug($e);
|
||||
}
|
||||
|
||||
} else {
|
||||
Storage::put('private_uploads/users/'.$file_name, file_get_contents($file));
|
||||
}
|
||||
|
||||
//Log the uploaded file to the log
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $user->id;
|
||||
@@ -48,7 +73,7 @@ class UserFilesController extends Controller
|
||||
$logAction->note = $request->input('notes');
|
||||
$logAction->target_id = null;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->filename = $filename;
|
||||
$logAction->filename = $file_name;
|
||||
$logAction->action_type = 'uploaded';
|
||||
|
||||
if (!$logAction->save()) {
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
use Auth;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Str;
|
||||
@@ -483,7 +484,6 @@ class UsersController extends Controller
|
||||
$user->first_name = '';
|
||||
$user->last_name = '';
|
||||
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
|
||||
|
||||
$user->id = null;
|
||||
|
||||
// Get this user groups
|
||||
@@ -617,4 +617,31 @@ class UsersController extends Controller
|
||||
->with('show_user', $show_user)
|
||||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send individual password reset email
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since [v5.0.15]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function sendPasswordReset($id) {
|
||||
|
||||
if (($user = User::find($id)) && ($user->activated == '1') && ($user->email!='') && ($user->ldap_import == '0')) {
|
||||
$credentials = ['email' => $user->email];
|
||||
|
||||
try {
|
||||
\Password::sendResetLink($credentials, function (Message $message) use ($user) {
|
||||
$message->subject($this->getEmailSubject());
|
||||
});
|
||||
return redirect()->back()->with('success', trans('admin/users/message.password_reset_sent', ['email' => $user->email]));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->back()->with('error', ' Error sending email. :( ');
|
||||
}
|
||||
|
||||
}
|
||||
return redirect()->back()->with('error', 'User is not activated, is LDAP synced, or does not have an email address ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ class ViewAssetsController extends Controller
|
||||
$logaction->logaction('request canceled');
|
||||
$settings->notify(new RequestAssetCancelation($data));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('success')->with('success', trans('admin/hardware/message.requests.cancel-success'));
|
||||
->with('success')->with('success', trans('admin/hardware/message.requests.cancel'));
|
||||
}
|
||||
|
||||
$logaction->logaction('requested');
|
||||
|
||||
@@ -23,6 +23,7 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\CheckForDebug::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\SecurityHeaders::class,
|
||||
\App\Http\Middleware\PreventBackHistory::class,
|
||||
|
||||
];
|
||||
|
||||
|
||||
30
app/Http/Middleware/PreventBackHistory.php
Normal file
30
app/Http/Middleware/PreventBackHistory.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class PreventBackHistory
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$headers = [
|
||||
'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
'Expires' => 'Sun, 02 Jan 1990 00:00:00 GMT'
|
||||
];
|
||||
$response = $next($request);
|
||||
foreach($headers as $key => $value) {
|
||||
$response->headers->set($key, $value);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class SecurityHeaders
|
||||
// We have to exclude debug mode here because debugbar pulls from a CDN or two
|
||||
// and it will break things.
|
||||
|
||||
if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) {
|
||||
if ((config('app.debug')!='true') && (config('app.enable_csp')=='true')) {
|
||||
$csp_policy[] = "default-src 'self'";
|
||||
$csp_policy[] = "style-src 'self' 'unsafe-inline'";
|
||||
$csp_policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
|
||||
|
||||
@@ -24,7 +24,7 @@ class AssetFileRequest extends Request
|
||||
{
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf|max:'.$max_file_size,
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,webp|max:'.$max_file_size,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ namespace App\Http\Requests;
|
||||
use App\Models\SnipeModel;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use App\Http\Traits\ConvertsBase64ToFiles;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Storage;
|
||||
|
||||
class ImageUploadRequest extends Request
|
||||
{
|
||||
use ConvertsBase64ToFiles;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@@ -26,16 +30,32 @@ class ImageUploadRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
|
||||
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
|
||||
];
|
||||
|
||||
return [
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
|
||||
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
|
||||
];
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
{
|
||||
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields that should be traited from base64 to files
|
||||
*/
|
||||
protected function base64FileKeys(): array
|
||||
{
|
||||
/**
|
||||
* image_source is here just legacy reasons. Api\AssetController
|
||||
* had it once to allow encoded image uploads.
|
||||
*/
|
||||
return [
|
||||
'image' => 'auto',
|
||||
'image_source' => 'auto'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle and store any images attached to request
|
||||
@@ -77,14 +97,22 @@ class ImageUploadRequest extends Request
|
||||
\Log::debug('Form fieldname is: '.$form_fieldname);
|
||||
\Log::debug('DB fieldname is: '.$use_db_field);
|
||||
\Log::debug('Trying to upload to '. $path);
|
||||
|
||||
// ConvertBase64ToFiles just changes object type,
|
||||
// as it cannot currently insert files to $this->files
|
||||
if ($this->offsetGet($form_fieldname) instanceof UploadedFile) {
|
||||
$image=$this->offsetGet($form_fieldname);
|
||||
} else {
|
||||
if ($this->hasFile($form_fieldname)) {
|
||||
$image = $this->file($form_fieldname);
|
||||
}
|
||||
}
|
||||
|
||||
\Log::debug($this->file());
|
||||
|
||||
if ($this->hasFile($form_fieldname)) {
|
||||
if (isset($image)) {
|
||||
\Log::debug($image);
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
$image = $this->file($form_fieldname);
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = $type.'-'.$form_fieldname.'-'.str_random(10).'.'.$ext;
|
||||
|
||||
@@ -165,4 +193,5 @@ class ImageUploadRequest extends Request
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,14 @@ abstract class Request extends FormRequest
|
||||
{
|
||||
protected $rules = [];
|
||||
|
||||
public function json($key = null, $default = null)
|
||||
{
|
||||
if ($this->ajax() || $this->wantsJson()) {
|
||||
json_decode($this->getContent(), false, 512, JSON_THROW_ON_ERROR); // ignore output, just throw
|
||||
}
|
||||
return parent::json($key, $default);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return $this->rules;
|
||||
|
||||
33
app/Http/Requests/SlackSettingsRequest.php
Normal file
33
app/Http/Requests/SlackSettingsRequest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
class SlackSettingsRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'slack_endpoint' => 'url|required_with:slack_channel|starts_with:"https://hooks.slack.com"|nullable',
|
||||
'slack_channel' => 'required_with:slack_endpoint|starts_with:#|nullable',
|
||||
'slack_botname' => 'string|nullable',
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
80
app/Http/Traits/ConvertsBase64ToFiles.php
Normal file
80
app/Http/Traits/ConvertsBase64ToFiles.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Traits;
|
||||
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait ConvertsBase64ToFiles
|
||||
{
|
||||
protected function base64FileKeys(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls the Base64 contents for each file key and creates
|
||||
* an UploadedFile instance from it and sets it on the
|
||||
* request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$flattened = Arr::dot($this->base64FileKeys());
|
||||
|
||||
Collection::make($flattened)->each(function ($filename, $key) {
|
||||
rescue(function () use ($key, $filename) {
|
||||
// dont process plain files
|
||||
if ( $this->file($key)){
|
||||
return;
|
||||
}
|
||||
|
||||
$base64Contents = $this->input($key);
|
||||
|
||||
if (!$base64Contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
// autogenerate filenames
|
||||
if ($filename == 'auto'){
|
||||
$header = explode(';', $base64Contents, 2)[0];
|
||||
// Grab the image type from the header while we're at it.
|
||||
$filename = $key . '.' . substr($header, strpos($header, '/')+1);
|
||||
}
|
||||
|
||||
// Generate a temporary path to store the Base64 contents
|
||||
$tempFilePath = tempnam(sys_get_temp_dir(), $filename);
|
||||
|
||||
// Store the contents using a stream, or by decoding manually
|
||||
if (Str::startsWith($base64Contents, 'data:') && count(explode(',', $base64Contents)) > 1) {
|
||||
$source = fopen($base64Contents, 'r');
|
||||
$destination = fopen($tempFilePath, 'w');
|
||||
|
||||
stream_copy_to_stream($source, $destination);
|
||||
|
||||
fclose($source);
|
||||
fclose($destination);
|
||||
} else {
|
||||
file_put_contents($tempFilePath, base64_decode($base64Contents, true));
|
||||
}
|
||||
|
||||
$uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true);
|
||||
|
||||
\Log::debug("Trait: uploadedfile ". $tempFilePath);
|
||||
$this->offsetUnset($key);
|
||||
\Log::debug("Trait: encoded field \"$key\" removed" );
|
||||
|
||||
//Inserting new file to $this-files does not work so have to deal this after
|
||||
$this->offsetSet($key,$uploadedFile);
|
||||
\Log::debug("Trait: field \"$key\" inserted as UplodedFile" );
|
||||
|
||||
}, null, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loosely based on idea https://github.com/protonemedia/laravel-mixins/tree/master/src/Request
|
||||
* */
|
||||
@@ -82,7 +82,7 @@ class AccessoriesTransformer
|
||||
'first_name'=> e($user->first_name),
|
||||
'last_name'=> e($user->last_name),
|
||||
'employee_number' => e($user->employee_num),
|
||||
'checkout_notes' => $user->pivot->note,
|
||||
'checkout_notes' => e($user->pivot->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
|
||||
'type' => 'user',
|
||||
'available_actions' => ['checkin' => true]
|
||||
|
||||
@@ -36,7 +36,11 @@ class ActionlogsTransformer
|
||||
|
||||
if (is_array($meta_value)) {
|
||||
foreach ($meta_value as $meta_value_key => $meta_value_value) {
|
||||
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
|
||||
if (is_scalar($meta_value_value)) {
|
||||
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
|
||||
} else {
|
||||
$clean_meta[$key][$meta_value_key] = 'invalid scalar: '.print_r($meta_value_value, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -115,7 +119,7 @@ class ActionlogsTransformer
|
||||
'note' => ($actionlog->note) ? e($actionlog->note): null,
|
||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): null,
|
||||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||
|
||||
];
|
||||
|
||||
|
||||
@@ -93,15 +93,15 @@ class AssetsTransformer
|
||||
$value = (Gate::allows('superadmin')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted'));
|
||||
|
||||
$fields_array[$field->name] = [
|
||||
'field' => $field->convertUnicodeDbSlug(),
|
||||
'value' => $value,
|
||||
'field' => e($field->convertUnicodeDbSlug()),
|
||||
'value' => e($value),
|
||||
'field_format' => $field->format,
|
||||
];
|
||||
|
||||
} else {
|
||||
$fields_array[$field->name] = [
|
||||
'field' => $field->convertUnicodeDbSlug(),
|
||||
'value' => $asset->{$field->convertUnicodeDbSlug()},
|
||||
'field' => e($field->convertUnicodeDbSlug()),
|
||||
'value' => e($asset->{$field->convertUnicodeDbSlug()}),
|
||||
'field_format' => $field->format,
|
||||
];
|
||||
|
||||
@@ -114,26 +114,38 @@ class AssetsTransformer
|
||||
}
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => Gate::allows('checkout', Asset::class),
|
||||
'checkin' => Gate::allows('checkin', Asset::class),
|
||||
'clone' => Gate::allows('create', Asset::class),
|
||||
'restore' => false,
|
||||
'update' => (bool) Gate::allows('update', Asset::class),
|
||||
'delete' => ($asset->assigned_to=='' && Gate::allows('delete', Asset::class)),
|
||||
'checkout' => ($asset->deleted_at=='' && Gate::allows('checkout', Asset::class)) ? true : false,
|
||||
'checkin' => ($asset->deleted_at=='' && Gate::allows('checkin', Asset::class)) ? true : false,
|
||||
'clone' => Gate::allows('create', Asset::class) ? true : false,
|
||||
'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false,
|
||||
'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false,
|
||||
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class)) ? true : false,
|
||||
];
|
||||
|
||||
if ($asset->deleted_at!='') {
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => true,
|
||||
'checkin' => false,
|
||||
'clone' => Gate::allows('create', Asset::class),
|
||||
'restore' => Gate::allows('create', Asset::class),
|
||||
'update' => false,
|
||||
'delete' => false,
|
||||
];
|
||||
|
||||
|
||||
if (request('components')=='true') {
|
||||
|
||||
if ($asset->components) {
|
||||
$array['components'] = [];
|
||||
|
||||
foreach ($asset->components as $component) {
|
||||
$array['components'][] = [
|
||||
|
||||
'id' => $component->id,
|
||||
'pivot_id' => $component->pivot->id,
|
||||
'name' => e($component->name),
|
||||
'qty' => $component->pivot->assigned_qty,
|
||||
'price_cost' => $component->purchase_cost,
|
||||
'purchase_total' => $component->purchase_cost * $component->pivot->assigned_qty,
|
||||
'checkout_date' => Helper::getFormattedDateObject($component->pivot->created_at, 'datetime') ,
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
@@ -158,7 +170,7 @@ class AssetsTransformer
|
||||
}
|
||||
return $asset->assigned ? [
|
||||
'id' => $asset->assigned->id,
|
||||
'name' => $asset->assigned->display_name,
|
||||
'name' => e($asset->assigned->display_name),
|
||||
'type' => $asset->assignedType()
|
||||
] : null;
|
||||
}
|
||||
@@ -197,4 +209,4 @@ class AssetsTransformer
|
||||
return $array;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class CategoriesTransformer
|
||||
'image' => ($category->image) ? Storage::disk('public')->url('categories/'.e($category->image)) : null,
|
||||
'category_type' => ucwords(e($category->category_type)),
|
||||
'has_eula' => ($category->getEula() ? true : false),
|
||||
'use_default_eula' => ($category->use_default_eula=='1' ? true : false),
|
||||
'eula' => ($category->getEula()),
|
||||
'checkin_email' => ($category->checkin_email =='1'),
|
||||
'require_acceptance' => ($category->require_acceptance == '1'),
|
||||
|
||||
120
app/Http/Transformers/DepreciationReportTransformer.php
Normal file
120
app/Http/Transformers/DepreciationReportTransformer.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
/**
|
||||
* This tranformer looks like it's extraneous, since we return as much or more
|
||||
* info in the AssetsTransformer, but we want to flatten these results out so that they
|
||||
* don't dislose more information than we want. Folks with depreciation powers don't necessaily
|
||||
* have the right to see additional info, and inspecting the API call here could disclose
|
||||
* info they're not supposed to see.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.2.0]
|
||||
*/
|
||||
class DepreciationReportTransformer
|
||||
{
|
||||
public function transformAssets(Collection $assets, $total)
|
||||
{
|
||||
$array = array();
|
||||
foreach ($assets as $asset) {
|
||||
$array[] = self::transformAsset($asset);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformAsset(Asset $asset)
|
||||
{
|
||||
|
||||
/**
|
||||
* Set some default values here
|
||||
*/
|
||||
$purchase_cost = null;
|
||||
$depreciated_value = null;
|
||||
$monthly_depreciation = null;
|
||||
$diff = null;
|
||||
$checkout_target = null;
|
||||
|
||||
/**
|
||||
* If there is a location set and a currency set, use that for display
|
||||
*/
|
||||
if ($asset->location && $asset->location->currency) {
|
||||
$purchase_cost_currency = $asset->location->currency;
|
||||
} else {
|
||||
$purchase_cost_currency = \App\Models\Setting::getSettings()->default_currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a NOT an empty purchase cost (meaning not null or '' but it *could* be zero),
|
||||
* format the purchase cost. We coould do this inline in the transformer, but we need that value
|
||||
* for the other calculations that come after, like diff, etc.
|
||||
*/
|
||||
if ($asset->purchase_cost!='') {
|
||||
$purchase_cost = $asset->purchase_cost;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override the previously set null values if there is a valid model and associated depreciation
|
||||
*/
|
||||
if (($asset->model) && ($asset->model->depreciation)) {
|
||||
$depreciated_value = \App\Helpers\Helper::formatCurrencyOutput($asset->getDepreciatedValue());
|
||||
$monthly_depreciation = \App\Helpers\Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0));
|
||||
$diff = \App\Helpers\Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
|
||||
}
|
||||
|
||||
if ($asset->assigned) {
|
||||
$checkout_target = $asset->assigned->name;
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$checkout_target = $asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$array = [
|
||||
|
||||
'company' => ($asset->company) ? e($asset->company->name) : null,
|
||||
'name' => e($asset->name),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
'model' => ($asset->model) ? e($asset->model->name) : null,
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'eol' => ($asset->purchase_date!='') ? Helper::getFormattedDateObject($asset->present()->eol_date(), 'date') : null ,
|
||||
'status_label' => ($asset->assetstatus) ? e($asset->assetstatus->name) : null,
|
||||
'status' => ($asset->assetstatus) ? e($asset->present()->statusMeta) : null,
|
||||
'category' => (($asset->model) && ($asset->model->category)) ? e($asset->model->category->name) : null,
|
||||
'manufacturer' => (($asset->model) && ($asset->model->manufacturer)) ? e($asset->model->manufacturer->name) : null,
|
||||
'supplier' => ($asset->supplier) ? e($asset->supplier->name) : null,
|
||||
'notes' => ($asset->notes) ? e($asset->notes) : null,
|
||||
'order_number' => ($asset->order_number) ? e($asset->order_number) : null,
|
||||
'location' => ($asset->location) ? e($asset->location->name) : null,
|
||||
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
|
||||
'currency' => $purchase_cost_currency,
|
||||
'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost),
|
||||
'book_value' => Helper::formatCurrencyOutput($depreciated_value),
|
||||
'monthly_depreciation' => $monthly_depreciation,
|
||||
'checked_out_to' => $checkout_target,
|
||||
'diff' => Helper::formatCurrencyOutput($diff),
|
||||
'number_of_months' => ($asset->model && $asset->model->depreciation) ? e($asset->model->depreciation->months) : null,
|
||||
'depreciation' => (($asset->model) && ($asset->model->depreciation)) ? e($asset->model->depreciation->name) : null,
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformAssetsDatatable($assets)
|
||||
{
|
||||
return (new DatatablesTransformer)->transformDatatables($assets);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class DepreciationsTransformer
|
||||
'id' => (int) $depreciation->id,
|
||||
'name' => e($depreciation->name),
|
||||
'months' => $depreciation->months . ' '. trans('general.months'),
|
||||
'depreciation_min' => $depreciation->depreciation_min,
|
||||
'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime'),
|
||||
];
|
||||
|
||||
@@ -9,13 +9,13 @@ use Illuminate\Database\Eloquent\Collection;
|
||||
class GroupsTransformer
|
||||
{
|
||||
|
||||
public function transformGroups (Collection $groups)
|
||||
public function transformGroups (Collection $groups, $total = null)
|
||||
{
|
||||
$array = array();
|
||||
foreach ($groups as $group) {
|
||||
$array[] = self::transformGroup($group);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array);
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformGroup (Group $group)
|
||||
|
||||
@@ -31,7 +31,8 @@ class LicensesTransformer
|
||||
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
|
||||
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
|
||||
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
||||
'purchase_cost' => e($license->purchase_cost),
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost),
|
||||
'purchase_cost_numeric' => $license->purchase_cost,
|
||||
'notes' => e($license->notes),
|
||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||
'seats' => (int) $license->seats,
|
||||
|
||||
@@ -14,7 +14,7 @@ class AccessoryImporter extends ItemImporter
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row); // TODO: Change the autogenerated stub
|
||||
$this->createAccessoryIfNotExists();
|
||||
$this->createAccessoryIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ class AccessoryImporter extends ItemImporter
|
||||
* @author Daniel Melzter
|
||||
* @since 3.0
|
||||
*/
|
||||
public function createAccessoryIfNotExists()
|
||||
public function createAccessoryIfNotExists($row)
|
||||
{
|
||||
$accessory = Accessory::where('name', $this->item['name'])->first();
|
||||
if ($accessory) {
|
||||
@@ -33,12 +33,14 @@ class AccessoryImporter extends ItemImporter
|
||||
}
|
||||
|
||||
$this->log('Updating Accessory');
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");
|
||||
$accessory->update($this->sanitizeItemForUpdating($accessory));
|
||||
$accessory->save();
|
||||
return;
|
||||
}
|
||||
$this->log("No Matching Accessory, Creating a new one");
|
||||
$accessory = new Accessory();
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");
|
||||
$accessory->fill($this->sanitizeItemForStoring($accessory));
|
||||
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
|
||||
@@ -41,9 +41,12 @@ class ConsumableImporter extends ItemImporter
|
||||
$consumable = new Consumable();
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");;
|
||||
$this->item['item_no'] = $this->findCsvMatch($row, "item_number");
|
||||
$this->item['min_amt'] = $this->findCsvMatch($row, "minimum quantity");
|
||||
$this->log("min_amt " . $this->item["min_amt"]);
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$consumable->unsetEventDispatcher();
|
||||
$this->log(implode(",", $this->item));
|
||||
if ($consumable->save()) {
|
||||
$consumable->logCreate('Imported using CSV Importer');
|
||||
$this->log("Consumable " . $this->item["name"] . ' was created');
|
||||
|
||||
@@ -75,6 +75,7 @@ abstract class Importer
|
||||
'department' => 'department',
|
||||
'manager_first_name' => 'manager first name',
|
||||
'manager_last_name' => 'manager last name',
|
||||
'min_amt' => 'minimum quantity',
|
||||
];
|
||||
/**
|
||||
* Map of item fields->csv names
|
||||
@@ -195,11 +196,11 @@ abstract class Importer
|
||||
$val = $default;
|
||||
$key = $this->lookupCustomKey($key);
|
||||
|
||||
// $this->log("Custom Key: ${key}");
|
||||
$this->log("Custom Key: ${key}");
|
||||
if (array_key_exists($key, $array)) {
|
||||
$val = Encoding::toUTF8(trim($array[ $key ]));
|
||||
}
|
||||
// $this->log("${key}: ${val}");
|
||||
$this->log("${key}: ${val}");
|
||||
return $val;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class ItemImporter extends Importer
|
||||
return $this->createOrFetchUser($row);
|
||||
}
|
||||
|
||||
if ($this->item['checkout_class'] === 'location') {
|
||||
if (strtolower($this->item['checkout_class']) === 'location') {
|
||||
return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location')));
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ class ItemImporter extends Importer
|
||||
*/
|
||||
public function createOrFetchAssetModel(array $row)
|
||||
{
|
||||
|
||||
$condition = array();
|
||||
$asset_model_name = $this->findCsvMatch($row, "asset_model");
|
||||
$asset_modelNumber = $this->findCsvMatch($row, "model_number");
|
||||
// TODO: At the moment, this means we can't update the model number if the model name stays the same.
|
||||
@@ -189,8 +189,16 @@ class ItemImporter extends Importer
|
||||
} elseif ((empty($asset_model_name)) && (empty($asset_modelNumber))) {
|
||||
$asset_model_name ='Unknown';
|
||||
}
|
||||
|
||||
if ((!empty($asset_model_name)) && (empty($asset_modelNumber))) {
|
||||
$condition[] = ['name', '=', $asset_model_name];
|
||||
} elseif ((!empty($asset_model_name)) && (!empty($asset_modelNumber))) {
|
||||
$condition[] = ['name', '=', $asset_model_name];
|
||||
$condition[] = ['model_number', '=', $asset_modelNumber];
|
||||
}
|
||||
|
||||
$editingModel = $this->updating;
|
||||
$asset_model = AssetModel::where(['name' => $asset_model_name, 'model_number' => $asset_modelNumber])->first();
|
||||
$asset_model = AssetModel::where($condition)->first();
|
||||
|
||||
if ($asset_model) {
|
||||
if (!$this->updating) {
|
||||
@@ -200,7 +208,11 @@ class ItemImporter extends Importer
|
||||
$this->log("Matching Model found, updating it.");
|
||||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
$item['name'] = $asset_model_name;
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
|
||||
if(!empty($asset_modelNumber)){
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
}
|
||||
|
||||
$asset_model->update($item);
|
||||
$asset_model->save();
|
||||
$this->log("Asset Model Updated");
|
||||
|
||||
@@ -32,7 +32,6 @@ class LicenseImporter extends ItemImporter
|
||||
{
|
||||
$editingLicense = false;
|
||||
$license = License::where('name', $this->item['name'])
|
||||
->where('serial', $this->item['serial'])
|
||||
->first();
|
||||
if ($license) {
|
||||
if (!$this->updating) {
|
||||
|
||||
@@ -52,6 +52,7 @@ class UserImporter extends ItemImporter
|
||||
$this->item['city'] = $this->findCsvMatch($row, 'city');
|
||||
$this->item['state'] = $this->findCsvMatch($row, 'state');
|
||||
$this->item['country'] = $this->findCsvMatch($row, 'country');
|
||||
$this->item['zip'] = $this->findCsvMatch($row, 'zip');
|
||||
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
|
||||
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department'));
|
||||
@@ -121,12 +122,15 @@ class UserImporter extends ItemImporter
|
||||
*/
|
||||
public function createOrFetchDepartment($department_name)
|
||||
{
|
||||
if (is_null($department_name) || $department_name == ''){
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
$department = Department::where(['name' => $department_name])->first();
|
||||
if ($department) {
|
||||
$this->log('A matching department ' . $department_name . ' already exists');
|
||||
return $department->id;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$department = new department();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
| serial number | serial | Asset, license |
|
||||
| status | status | Asset ? All |
|
||||
| supplier | supplier | Asset ? All |
|
||||
| minimum quantity | min_amt | Consumable |
|
||||
| termination date | termination_date | License |
|
||||
| warranty months | warranty_months | Asset |
|
||||
| User Related Fields | assigned_to | Asset |
|
||||
|
||||
@@ -71,20 +71,27 @@ class CheckoutableListener
|
||||
/**
|
||||
* Send the appropriate notification
|
||||
*/
|
||||
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
|
||||
->where('assigned_to_id', $event->checkedOutTo->id)
|
||||
->get();
|
||||
|
||||
|
||||
foreach($acceptances as $acceptance){
|
||||
if($acceptance->isPending()){
|
||||
$acceptance->delete();
|
||||
}
|
||||
}
|
||||
\Log::debug('checked out to a user');
|
||||
if(!$event->checkedOutTo->locale){
|
||||
\Log::debug('Use default settings locale');
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$this->getNotifiables($event),
|
||||
$this->getNotifiables($event),
|
||||
$this->getCheckinNotification($event)
|
||||
);
|
||||
} else {
|
||||
\Log::debug('Use user locale? I do not think this works as expected yet');
|
||||
// \Log::debug(print_r($this->getNotifiables($event), true));
|
||||
Notification::send(
|
||||
$this->getNotifiables($event),
|
||||
$this->getNotifiables($event),
|
||||
$this->getCheckinNotification($event)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ class Accessory extends SnipeModel
|
||||
'supplier_id',
|
||||
'image',
|
||||
'qty',
|
||||
'min_amt',
|
||||
'requestable'
|
||||
];
|
||||
|
||||
@@ -377,4 +378,17 @@ class Accessory extends SnipeModel
|
||||
{
|
||||
return $query->leftJoin('manufacturers', 'accessories.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on supplier
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderSupplier($query, $order)
|
||||
{
|
||||
return $query->leftJoin('suppliers', 'accessories.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ use Watson\Validating\ValidatingTrait;
|
||||
class Asset extends Depreciable
|
||||
{
|
||||
protected $presenter = 'App\Presenters\AssetPresenter';
|
||||
use CompanyableTrait;
|
||||
use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait;
|
||||
|
||||
const LOCATION = 'location';
|
||||
@@ -180,11 +181,6 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function save(array $params = [])
|
||||
{
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
// I don't remember why we have this here? Asset tag would always be required, even if auto increment is on...
|
||||
$this->rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
|
||||
|
||||
if($this->model_id != '') {
|
||||
$model = AssetModel::find($this->model_id);
|
||||
|
||||
@@ -380,7 +376,7 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function components()
|
||||
{
|
||||
return $this->belongsToMany('\App\Models\Component', 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty')->withTrashed();
|
||||
return $this->belongsToMany('\App\Models\Component', 'components_assets', 'asset_id', 'component_id')->withPivot('id', 'assigned_qty', 'created_at')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
@@ -669,13 +665,15 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public static function getExpiringWarrantee($days = 30)
|
||||
{
|
||||
$days = (is_null($days)) ? 30 : $days;
|
||||
|
||||
return Asset::where('archived', '=', '0')
|
||||
->whereNotNull('warranty_months')
|
||||
->whereNotNull('purchase_date')
|
||||
->whereNull('deleted_at')
|
||||
->whereRaw(\DB::raw('DATE_ADD(`purchase_date`,INTERVAL `warranty_months` MONTH) <= DATE(NOW() + INTERVAL '
|
||||
. $days
|
||||
. ' DAY) AND DATE_ADD(`purchase_date`,INTERVAL `warranty_months` MONTH) > NOW()'))
|
||||
. ' DAY) AND DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) > NOW()'))
|
||||
->orderBy('purchase_date', 'ASC')
|
||||
->get();
|
||||
}
|
||||
@@ -818,7 +816,9 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function checkin_email()
|
||||
{
|
||||
return $this->model->category->checkin_email;
|
||||
if (($this->model) && ($this->model->category)) {
|
||||
return $this->model->category->checkin_email;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -890,7 +890,7 @@ class Asset extends Depreciable
|
||||
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%", "%$term%"]);
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%"]);
|
||||
|
||||
}
|
||||
|
||||
@@ -1260,7 +1260,7 @@ class Asset extends Depreciable
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%", "%$search%"])
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%"])
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
|
||||
|
||||
@@ -78,12 +78,12 @@ final class Company extends SnipeModel
|
||||
$company_id = null;
|
||||
}
|
||||
|
||||
$table = ($table_name) ? $table_name."." : '';
|
||||
$table = ($table_name) ? $table_name."." : $query->getModel()->getTable().".";
|
||||
|
||||
if(\Schema::hasColumn($query->getModel()->getTable(), $column)){
|
||||
return $query->where($table.$column, '=', $company_id);
|
||||
return $query->where($table.$column, '=', $company_id);
|
||||
} else {
|
||||
return $query->join('users as users_comp', 'users_comp.id', 'user_id')->where('users_comp.company_id', '=', $company_id);
|
||||
return $query->join('users as users_comp', 'users_comp.id', 'user_id')->where('users_comp.company_id', '=', $company_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ class Consumable extends SnipeModel
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
'qty',
|
||||
'min_amt',
|
||||
'requestable'
|
||||
];
|
||||
|
||||
@@ -78,7 +79,7 @@ class Consumable extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no'];
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
||||
@@ -27,7 +27,6 @@ class Ldap extends Model
|
||||
$ldap_server_cert_ignore = Setting::getSettings()->ldap_server_cert_ignore;
|
||||
$ldap_use_tls = Setting::getSettings()->ldap_tls;
|
||||
|
||||
|
||||
// If we are ignoring the SSL cert we need to setup the environment variable
|
||||
// before we create the connection
|
||||
if ($ldap_server_cert_ignore=='1') {
|
||||
@@ -50,10 +49,16 @@ class Ldap extends Model
|
||||
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
|
||||
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
|
||||
|
||||
if (Setting::getSettings()->ldap_client_tls_cert && Setting::getSettings()->ldap_client_tls_key) {
|
||||
ldap_set_option($connection, LDAP_OPT_X_TLS_CERTFILE, Setting::get_client_side_cert_path());
|
||||
ldap_set_option($connection, LDAP_OPT_X_TLS_KEYFILE, Setting::get_client_side_key_path());
|
||||
}
|
||||
|
||||
if ($ldap_use_tls=='1') {
|
||||
ldap_start_tls($connection);
|
||||
}
|
||||
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
|
||||
@@ -627,6 +627,7 @@ class License extends Depreciable
|
||||
*/
|
||||
public static function getExpiringLicenses($days = 60)
|
||||
{
|
||||
$days = (is_null($days)) ? 60 : $days;
|
||||
|
||||
return License::whereNotNull('expiration_date')
|
||||
->whereNull('deleted_at')
|
||||
|
||||
@@ -23,7 +23,7 @@ class Location extends SnipeModel
|
||||
protected $rules = array(
|
||||
'name' => 'required|min:2|max:255|unique_undeleted',
|
||||
'city' => 'min:2|max:255|nullable',
|
||||
'country' => 'min:2|max:2|nullable',
|
||||
'country' => 'min:2|max:255|nullable',
|
||||
'address' => 'max:80|nullable',
|
||||
'address2' => 'max:80|nullable',
|
||||
'zip' => 'min:3|max:10|nullable',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user