Compare commits
636 Commits
v7.1.14
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20a84c66a4 | ||
|
|
0fda3e2961 | ||
|
|
d6d467d1bc | ||
|
|
6e24c4294a | ||
|
|
25043283d7 | ||
|
|
a3081fdbaf | ||
|
|
218674b4cf | ||
|
|
6033485755 | ||
|
|
0a9bf97ae0 | ||
|
|
b05ab19cde | ||
|
|
4c7c48e38b | ||
|
|
e53ed2319c | ||
|
|
8f512e5941 | ||
|
|
8a1b6b0684 | ||
|
|
36f460d32b | ||
|
|
802fcbafa0 | ||
|
|
1098b8cd9d | ||
|
|
da8999f59a | ||
|
|
1212267da3 | ||
|
|
d696ed8a5a | ||
|
|
c3d17c5727 | ||
|
|
cb25d0f2f3 | ||
|
|
be535671bc | ||
|
|
63838f6e74 | ||
|
|
5ddf0dd789 | ||
|
|
b003890dd2 | ||
|
|
8db8d4beba | ||
|
|
d5309c7d94 | ||
|
|
2668960bb9 | ||
|
|
8e4878fac0 | ||
|
|
752d89d177 | ||
|
|
0d7304eb8b | ||
|
|
5afcd8ddb3 | ||
|
|
5273408631 | ||
|
|
713c9fdd6f | ||
|
|
0a5c41ce23 | ||
|
|
48a916ab77 | ||
|
|
67ab602e3b | ||
|
|
801328ec42 | ||
|
|
4fe83467ee | ||
|
|
27a7a89990 | ||
|
|
446c7fb483 | ||
|
|
bbb2af7f53 | ||
|
|
d65206c7fe | ||
|
|
327491c3a4 | ||
|
|
02eeb7f916 | ||
|
|
e9ad43afbe | ||
|
|
434068a2ed | ||
|
|
7e65d68392 | ||
|
|
b3d35beeb9 | ||
|
|
fed7c2dc16 | ||
|
|
79951c3f17 | ||
|
|
221ffb446c | ||
|
|
57f80290a1 | ||
|
|
4b2ede7a71 | ||
|
|
5e465fa417 | ||
|
|
de5d66f5d2 | ||
|
|
a50c8c6269 | ||
|
|
96379d0a62 | ||
|
|
bbc9ed5778 | ||
|
|
1d0c156d2e | ||
|
|
7de5e2e7ef | ||
|
|
8d935b34eb | ||
|
|
9830959f11 | ||
|
|
37b39956b5 | ||
|
|
9a4fd81c70 | ||
|
|
0bb2e98af3 | ||
|
|
1c8d94c953 | ||
|
|
ca11efd3ee | ||
|
|
4a6a94a50e | ||
|
|
2d65b38155 | ||
|
|
587449ef97 | ||
|
|
06ffac9d7d | ||
|
|
e32ed03f66 | ||
|
|
a7543b407f | ||
|
|
98b1ba1e39 | ||
|
|
e03ed7567a | ||
|
|
9f0581275b | ||
|
|
3f5c166417 | ||
|
|
cf091377b6 | ||
|
|
0cc7a7014f | ||
|
|
4790ad53e3 | ||
|
|
62dc075834 | ||
|
|
41da04cd22 | ||
|
|
0c4fc56b80 | ||
|
|
907f0553d4 | ||
|
|
224f04db6c | ||
|
|
f0bcf78941 | ||
|
|
c08a4a2b0a | ||
|
|
8597984787 | ||
|
|
001ccf1ce9 | ||
|
|
082a974ee3 | ||
|
|
152c23b8f3 | ||
|
|
b635822a85 | ||
|
|
ec85e4b5b0 | ||
|
|
1acb7e0fe4 | ||
|
|
5914004a57 | ||
|
|
c5afc66d46 | ||
|
|
bc69660247 | ||
|
|
71c7c64fde | ||
|
|
c284ac5287 | ||
|
|
639afe5b9b | ||
|
|
049b9c542b | ||
|
|
92762896ef | ||
|
|
c699baf519 | ||
|
|
97b765b5cc | ||
|
|
40b41e646d | ||
|
|
63853db450 | ||
|
|
48dd3252cb | ||
|
|
ee6f60e63c | ||
|
|
f6abf90ba0 | ||
|
|
83ee0e0fb6 | ||
|
|
94f44d1b77 | ||
|
|
e12c7473f8 | ||
|
|
407d69b370 | ||
|
|
774e795d97 | ||
|
|
f698f74d3e | ||
|
|
80f9159f4d | ||
|
|
0ec25d55a0 | ||
|
|
bb03e00279 | ||
|
|
4d3db2ab44 | ||
|
|
c3d52af546 | ||
|
|
9672a13402 | ||
|
|
9564f7fdb8 | ||
|
|
eba1f03363 | ||
|
|
c7ca4d061f | ||
|
|
115f08ee49 | ||
|
|
e33c680679 | ||
|
|
1e8ec783b4 | ||
|
|
a6ed958687 | ||
|
|
4354f50168 | ||
|
|
8fc1227974 | ||
|
|
d410f168bd | ||
|
|
b38f9ad33c | ||
|
|
f8311815ee | ||
|
|
3edb501973 | ||
|
|
49918d3302 | ||
|
|
876ab44a16 | ||
|
|
15296d2b1c | ||
|
|
1434522149 | ||
|
|
23af5fb06e | ||
|
|
6b8c1eb523 | ||
|
|
0984194ec6 | ||
|
|
26264e1d55 | ||
|
|
dc5dedd5a3 | ||
|
|
d49bfb562a | ||
|
|
f90dd9d74d | ||
|
|
8d24c0af0d | ||
|
|
174a01cb6b | ||
|
|
360e0ff534 | ||
|
|
4f2721e93f | ||
|
|
0dce3b8b8c | ||
|
|
5042c2b30a | ||
|
|
b45cf6124f | ||
|
|
c8f280ac90 | ||
|
|
7eb936883a | ||
|
|
c3b5a92a48 | ||
|
|
eb054897d6 | ||
|
|
f6dbda4056 | ||
|
|
5992d2a1d2 | ||
|
|
a53976967a | ||
|
|
1be7508340 | ||
|
|
af135fa42c | ||
|
|
7aa5195e87 | ||
|
|
04c3481734 | ||
|
|
582b462326 | ||
|
|
b233715796 | ||
|
|
b93fc80011 | ||
|
|
41a6b34768 | ||
|
|
17a6a871ae | ||
|
|
5fceef1dc3 | ||
|
|
9b04d2e51c | ||
|
|
51b426f0b4 | ||
|
|
021c4f2598 | ||
|
|
ac96b8d4ae | ||
|
|
bc8719e336 | ||
|
|
bd38d64e66 | ||
|
|
74b5d6d12b | ||
|
|
ddd11939a5 | ||
|
|
3caa5f2042 | ||
|
|
5226d507d4 | ||
|
|
274b659905 | ||
|
|
a0b9714d72 | ||
|
|
36aea52ae0 | ||
|
|
e0643cd744 | ||
|
|
27fab0f573 | ||
|
|
183a4d49d8 | ||
|
|
edacc4eb54 | ||
|
|
1e7d7a147b | ||
|
|
8ee549efbf | ||
|
|
699476da90 | ||
|
|
d763feb803 | ||
|
|
d3bfa75251 | ||
|
|
833af55806 | ||
|
|
d1246c65bd | ||
|
|
38db890660 | ||
|
|
01c18f6303 | ||
|
|
c5e2aed164 | ||
|
|
4d1d2fedb7 | ||
|
|
ab6363a124 | ||
|
|
a0e7dcf4ff | ||
|
|
da33f1815a | ||
|
|
f089d1f0a4 | ||
|
|
72f58b0405 | ||
|
|
4bd6c2171c | ||
|
|
67494c1b0b | ||
|
|
3264149a2c | ||
|
|
8417fcb604 | ||
|
|
331fbb66bd | ||
|
|
400833f834 | ||
|
|
a7e6b8ea3f | ||
|
|
6e31d0f2c3 | ||
|
|
b9a660683c | ||
|
|
014350a26b | ||
|
|
06a0ac895d | ||
|
|
34a47e9e44 | ||
|
|
f5a9e4bafa | ||
|
|
66339481cf | ||
|
|
2e97b56deb | ||
|
|
8adb62fd3a | ||
|
|
43537d414b | ||
|
|
5014a95d9a | ||
|
|
f2fab57187 | ||
|
|
10a7ae8d47 | ||
|
|
5f0efd8cdb | ||
|
|
03c90d7b60 | ||
|
|
31155b5351 | ||
|
|
cc8f72c3f9 | ||
|
|
a0bab70def | ||
|
|
b5c8251539 | ||
|
|
0149ed75fd | ||
|
|
f72635955d | ||
|
|
5120cddda4 | ||
|
|
97398f1e68 | ||
|
|
281ff6ad5d | ||
|
|
9d49b01958 | ||
|
|
3f8916ea2e | ||
|
|
f6b9ae6aee | ||
|
|
de41def2b3 | ||
|
|
52b051e940 | ||
|
|
a137e31797 | ||
|
|
ca0f8ace99 | ||
|
|
6252f0ac5e | ||
|
|
b85b4b1e1b | ||
|
|
43d66a8fcc | ||
|
|
e5284c03e2 | ||
|
|
16283b8fc0 | ||
|
|
983b78edd9 | ||
|
|
2ad1407d51 | ||
|
|
ab67c48352 | ||
|
|
1d0d14876c | ||
|
|
14730184c9 | ||
|
|
8ff099e802 | ||
|
|
ea1f8328b9 | ||
|
|
99464fcd86 | ||
|
|
93d4d24194 | ||
|
|
1bbe9bf6c9 | ||
|
|
6874f703bf | ||
|
|
a7d5b3944e | ||
|
|
b5e83899c6 | ||
|
|
dcd586e3cd | ||
|
|
1246ab1de7 | ||
|
|
5a4a3aa8f3 | ||
|
|
2220828b00 | ||
|
|
716c67d4b8 | ||
|
|
ee4a54be24 | ||
|
|
46be1ada60 | ||
|
|
e1c0a80c20 | ||
|
|
2cb1b6d462 | ||
|
|
37ac7fe25c | ||
|
|
11f83b4cd9 | ||
|
|
0f1d10bd69 | ||
|
|
82108f8a18 | ||
|
|
a7dae10a82 | ||
|
|
2727210c78 | ||
|
|
fcbd5dcae5 | ||
|
|
fa80716320 | ||
|
|
15c9df0ab1 | ||
|
|
6ee0c93c87 | ||
|
|
37e091adbb | ||
|
|
021e82927e | ||
|
|
60642cd902 | ||
|
|
fd07550803 | ||
|
|
30b481bc93 | ||
|
|
25bfd3e845 | ||
|
|
0f18ee86f7 | ||
|
|
4a5f65616f | ||
|
|
f125c90c94 | ||
|
|
0d608552ef | ||
|
|
dd223fc215 | ||
|
|
87756cac66 | ||
|
|
9f49c25401 | ||
|
|
d0378070c8 | ||
|
|
9f395d1f6a | ||
|
|
feba860a90 | ||
|
|
424316df9f | ||
|
|
b7aedb7dcb | ||
|
|
8b38dc83f6 | ||
|
|
ad2b85393b | ||
|
|
85c1b0ec57 | ||
|
|
b99b56b32b | ||
|
|
2a21cce4f5 | ||
|
|
3a683d84a7 | ||
|
|
e05f443886 | ||
|
|
d65254c4ec | ||
|
|
6969b8be47 | ||
|
|
0a3bca078c | ||
|
|
9bbd802fce | ||
|
|
f932a4fc76 | ||
|
|
f7c1fc852d | ||
|
|
5522def054 | ||
|
|
8cd4b3303e | ||
|
|
d5c141dc59 | ||
|
|
7618169d79 | ||
|
|
eb026248a8 | ||
|
|
4304af0508 | ||
|
|
61eff02fcb | ||
|
|
cca76005a2 | ||
|
|
54f5f46e31 | ||
|
|
51a0767be9 | ||
|
|
da4c877ed8 | ||
|
|
e8cad0df69 | ||
|
|
59fe53842d | ||
|
|
1e513312eb | ||
|
|
99de639be4 | ||
|
|
b513575643 | ||
|
|
22602c7997 | ||
|
|
d10f118128 | ||
|
|
5a5f1082fb | ||
|
|
30a6a678d4 | ||
|
|
4384f8dcbb | ||
|
|
d576016a1c | ||
|
|
15a31bd141 | ||
|
|
5639a84d90 | ||
|
|
66d6b01307 | ||
|
|
889aff43c2 | ||
|
|
8f7223128f | ||
|
|
53cfcb7143 | ||
|
|
d60ac0c78d | ||
|
|
a328c7a64c | ||
|
|
f533cdc07a | ||
|
|
f5206078d4 | ||
|
|
0209d27081 | ||
|
|
d14f43fbbe | ||
|
|
b71a90a3c5 | ||
|
|
8d321edca8 | ||
|
|
3fb543bf03 | ||
|
|
e8d1093376 | ||
|
|
66e82a345a | ||
|
|
187e231c0e | ||
|
|
caf92748fc | ||
|
|
09b7db5103 | ||
|
|
5381c4e1e3 | ||
|
|
add3edba20 | ||
|
|
b4a2a4e1ec | ||
|
|
f2faf58786 | ||
|
|
124f9c84c2 | ||
|
|
96a4391d5c | ||
|
|
7b7a848a43 | ||
|
|
636e466b8a | ||
|
|
838a6d03bd | ||
|
|
8465806536 | ||
|
|
0f1f6bc6a5 | ||
|
|
95cd779334 | ||
|
|
0a7d1a27ee | ||
|
|
306c97b5b5 | ||
|
|
23038ab209 | ||
|
|
702621ec57 | ||
|
|
0be123b115 | ||
|
|
c093b4531d | ||
|
|
517e923f70 | ||
|
|
a0b8483878 | ||
|
|
075e3503b1 | ||
|
|
d427d9e5af | ||
|
|
940f1c2ecc | ||
|
|
3e131d515d | ||
|
|
364410a586 | ||
|
|
70d9ef428c | ||
|
|
4e28650ca9 | ||
|
|
8c6869fd34 | ||
|
|
7e7cbc4cc8 | ||
|
|
25163d1756 | ||
|
|
3982201d0e | ||
|
|
647ef6a4b7 | ||
|
|
a060f603b5 | ||
|
|
f7dcfca66c | ||
|
|
efeef5cdc4 | ||
|
|
9cb411c500 | ||
|
|
92f33c08f7 | ||
|
|
4841b89109 | ||
|
|
53de97db4e | ||
|
|
5574c5fa49 | ||
|
|
7373e2019c | ||
|
|
4bb19152c4 | ||
|
|
dc0b8c7572 | ||
|
|
b8265d54bb | ||
|
|
0e3efdfe87 | ||
|
|
363ec841d1 | ||
|
|
f8d0ddb3f7 | ||
|
|
4aa5961860 | ||
|
|
7862b74e99 | ||
|
|
c5710b858e | ||
|
|
8873137ed0 | ||
|
|
37d792352d | ||
|
|
52e4414bc5 | ||
|
|
f96b7c5056 | ||
|
|
82c38a8b18 | ||
|
|
2f5e468b3c | ||
|
|
4daa8e7c63 | ||
|
|
41f25341fd | ||
|
|
de77eda33d | ||
|
|
ee58fcc898 | ||
|
|
ae3cb7b37b | ||
|
|
5f83cb6a14 | ||
|
|
ae24b73b32 | ||
|
|
4aedbb52fa | ||
|
|
20dab4d89d | ||
|
|
e871481042 | ||
|
|
f10807661d | ||
|
|
d727b03d95 | ||
|
|
0cc3031864 | ||
|
|
59e6874a4a | ||
|
|
930ef3fd11 | ||
|
|
35f8a71c71 | ||
|
|
86762c5e90 | ||
|
|
3c75fc2ced | ||
|
|
702edf7908 | ||
|
|
54b4db86d2 | ||
|
|
0eadab49f1 | ||
|
|
45e98e0282 | ||
|
|
b97c54a54b | ||
|
|
e22296fd79 | ||
|
|
6c17d7d732 | ||
|
|
0d58ac61bc | ||
|
|
7c08fbe144 | ||
|
|
7777147af5 | ||
|
|
2042733dc0 | ||
|
|
d4cf392387 | ||
|
|
c881727747 | ||
|
|
ee00699cb3 | ||
|
|
b45749af16 | ||
|
|
54dec8d30d | ||
|
|
6f3fb47e4a | ||
|
|
7238238d1f | ||
|
|
84f6638f50 | ||
|
|
e390a95bd3 | ||
|
|
3616c92148 | ||
|
|
2fcc7e1188 | ||
|
|
8a06f4ad82 | ||
|
|
853e14f369 | ||
|
|
c1aa33862d | ||
|
|
30dc5fa0cf | ||
|
|
94e168fa15 | ||
|
|
4217d7402b | ||
|
|
3d28a9090c | ||
|
|
271de1eceb | ||
|
|
b6aae1f8a9 | ||
|
|
c313a78c3c | ||
|
|
37d65dac3d | ||
|
|
5ebf013d4e | ||
|
|
02c22c9efb | ||
|
|
d953519db6 | ||
|
|
dd97e4ea82 | ||
|
|
a20e03fce9 | ||
|
|
f0d3a6e2d3 | ||
|
|
5cb940c2ee | ||
|
|
5306e1cd15 | ||
|
|
299e743848 | ||
|
|
0c84904bf9 | ||
|
|
e00a1aec02 | ||
|
|
06e3bb7fd1 | ||
|
|
492e686b7a | ||
|
|
17706f150e | ||
|
|
6fef127cd1 | ||
|
|
f45b836010 | ||
|
|
925aea8531 | ||
|
|
bd6698de2a | ||
|
|
515f59fed9 | ||
|
|
5e74b109d9 | ||
|
|
8b643cb3b9 | ||
|
|
700647c53f | ||
|
|
081c5706c4 | ||
|
|
73a059c9ac | ||
|
|
4f9f035c69 | ||
|
|
cd3059f790 | ||
|
|
e974c96eda | ||
|
|
2004e58b53 | ||
|
|
eb2d7b1f4f | ||
|
|
47763d1e1a | ||
|
|
44447b85c9 | ||
|
|
b813dcd9d0 | ||
|
|
b69596a261 | ||
|
|
d02b9933e5 | ||
|
|
852a56fa9b | ||
|
|
9df78a9ed0 | ||
|
|
f3ad89931f | ||
|
|
21906d8c27 | ||
|
|
d1e2f9db34 | ||
|
|
3964296ae6 | ||
|
|
da9a61c28a | ||
|
|
6e16f589bd | ||
|
|
6b70443515 | ||
|
|
c9854d43a5 | ||
|
|
04708ae2b2 | ||
|
|
bbb9babf27 | ||
|
|
e5daf35f65 | ||
|
|
87c72953b2 | ||
|
|
89e2d03a81 | ||
|
|
fa5651f335 | ||
|
|
443447a068 | ||
|
|
b33c0fc4dd | ||
|
|
8e0b718a4a | ||
|
|
b51c505d9e | ||
|
|
8bba11e1bb | ||
|
|
4b54e980e2 | ||
|
|
033a56fe6d | ||
|
|
3682d9fa6c | ||
|
|
ce987b4f6d | ||
|
|
74efd850af | ||
|
|
d262638a63 | ||
|
|
bebb72a04f | ||
|
|
77c5035cac | ||
|
|
df8b1c0240 | ||
|
|
2901ecbf43 | ||
|
|
27c120a55e | ||
|
|
4e43fa6b9f | ||
|
|
0f0baa207d | ||
|
|
3ff1745f56 | ||
|
|
552f90ae2c | ||
|
|
d65c0c8bea | ||
|
|
2d5631284b | ||
|
|
3952fc10a6 | ||
|
|
0881301b6d | ||
|
|
530ea474d1 | ||
|
|
219540281f | ||
|
|
b74115b604 | ||
|
|
1630392953 | ||
|
|
861ef6312e | ||
|
|
f64aa4dfd4 | ||
|
|
786c41ad79 | ||
|
|
b24d80680e | ||
|
|
1a1f417633 | ||
|
|
bbfee27fd3 | ||
|
|
691e81d827 | ||
|
|
0ac1dd314a | ||
|
|
82f4cc799b | ||
|
|
4d8d069bbc | ||
|
|
5a396cc997 | ||
|
|
0883321d9e | ||
|
|
2768f19b7c | ||
|
|
20bd83232e | ||
|
|
d72970b5b6 | ||
|
|
9c1bea00ad | ||
|
|
8d8bf73c1b | ||
|
|
e5fb888d67 | ||
|
|
82df7a66ec | ||
|
|
0202a97e97 | ||
|
|
b34886ead6 | ||
|
|
6f6341bc09 | ||
|
|
5f8e91455f | ||
|
|
a5099b5163 | ||
|
|
27103124bf | ||
|
|
f2d34b2c03 | ||
|
|
25cba65c6e | ||
|
|
a756d2b765 | ||
|
|
740d46a50e | ||
|
|
d8d92a6d2c | ||
|
|
137193ab12 | ||
|
|
fcef60445c | ||
|
|
9e0897b2cb | ||
|
|
a23a3b95d6 | ||
|
|
1ac0be50a7 | ||
|
|
618bbc4bda | ||
|
|
8c434c7862 | ||
|
|
7f153b32e4 | ||
|
|
4fc8e8dd61 | ||
|
|
48e5ee2310 | ||
|
|
92e3a1e69e | ||
|
|
7f0e3e288e | ||
|
|
dc27e67b19 | ||
|
|
4c62e8ade9 | ||
|
|
87853921c3 | ||
|
|
8a496ccebc | ||
|
|
3e5f804791 | ||
|
|
62f8353bd7 | ||
|
|
1dd9273f70 | ||
|
|
2eeaef00e1 | ||
|
|
5c0c60a5b9 | ||
|
|
6fcbb108c6 | ||
|
|
71761a48ad | ||
|
|
495d74f7c9 | ||
|
|
fda77179a3 | ||
|
|
578495bab6 | ||
|
|
9a5c8c4ce6 | ||
|
|
0504c09a9a | ||
|
|
9d062f9849 | ||
|
|
0527201ae5 | ||
|
|
cf5c78029c | ||
|
|
26cc4497eb | ||
|
|
c35179b098 | ||
|
|
b2d0cbb264 | ||
|
|
ebf760a477 | ||
|
|
9fcb1a2d0e | ||
|
|
27bb938d9e | ||
|
|
75bd056bbe | ||
|
|
ad202be374 | ||
|
|
ca35b66597 | ||
|
|
e9e68171bb | ||
|
|
e791ebbe76 | ||
|
|
d92893b2c7 | ||
|
|
c68a2a36fa | ||
|
|
89c47c1879 | ||
|
|
b9cda88363 | ||
|
|
52028ddef2 | ||
|
|
c3845f4393 | ||
|
|
c9157dc55d | ||
|
|
c3b53b28e3 | ||
|
|
e636d7b9d5 | ||
|
|
bd86c5430c | ||
|
|
4a0bb31866 | ||
|
|
b7f6c7df06 | ||
|
|
327d27591f | ||
|
|
5041c07c7e | ||
|
|
505d601488 | ||
|
|
bca7f208a6 | ||
|
|
4f031149e8 | ||
|
|
b7011d853a | ||
|
|
06186c9b12 | ||
|
|
7a5faa9619 | ||
|
|
e5792fd415 | ||
|
|
45dbc02868 | ||
|
|
f1cc2c8d8b | ||
|
|
734af87f2f | ||
|
|
78d589fe78 | ||
|
|
c9fcc906fb | ||
|
|
f9fc2a44cd |
@@ -3226,6 +3226,51 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maciej-poleszczyk",
|
||||
"name": "maciej-poleszczyk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/133033121?v=4",
|
||||
"profile": "https://github.com/maciej-poleszczyk",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sgross-emlix",
|
||||
"name": "Sebastian Groß",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/143394709?v=4",
|
||||
"profile": "https://github.com/sgross-emlix",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AnouarTouati",
|
||||
"name": "Anouar Touati",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/41107778?v=4",
|
||||
"profile": "https://github.com/AnouarTouati",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aHVzY2g",
|
||||
"name": "aHVzY2g",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/25596663?v=4",
|
||||
"profile": "https://github.com/aHVzY2g",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "brlin-tw",
|
||||
"name": "林博仁 Buo-ren Lin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13408130?v=4",
|
||||
"profile": "https://brlin.me",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ MYSQL_ROOT_PASSWORD=changeme1234
|
||||
# REQUIRED: BASIC APP SETTINGS
|
||||
# --------------------------------------------
|
||||
APP_ENV=develop
|
||||
APP_DEBUG=false
|
||||
APP_DEBUG=true
|
||||
# please regenerate the APP_KEY value by calling `docker-compose run --rm snipeit bash` and then `php artisan key:generate --show` and then copy paste the value here
|
||||
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
|
||||
APP_URL=http://localhost:8000
|
||||
@@ -158,7 +158,7 @@ RESET_PASSWORD_LINK_EXPIRES=900
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MISC
|
||||
# --------------------------------------------
|
||||
LOG_CHANNEL=stderr
|
||||
LOG_CHANNEL=single
|
||||
LOG_MAX_DAYS=10
|
||||
APP_LOCKED=false
|
||||
APP_CIPHER=AES-256-CBC
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DOCKER SPECIFIC SETTINGS
|
||||
# --------------------------------------------
|
||||
APP_VERSION=v6.4.1
|
||||
APP_VERSION=
|
||||
APP_PORT=8000
|
||||
|
||||
# --------------------------------------------
|
||||
@@ -9,7 +9,7 @@ APP_PORT=8000
|
||||
# --------------------------------------------
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
# Please regenerate the APP_KEY value by calling `docker compose run --rm snipeit php artisan key:generate --show`. Copy paste the value here
|
||||
# Please regenerate the APP_KEY value by calling `docker compose run --rm app php artisan key:generate --show`. Copy paste the value here
|
||||
APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
|
||||
APP_URL=http://localhost:8000
|
||||
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier
|
||||
|
||||
@@ -80,6 +80,12 @@ MAIL_BACKUP_NOTIFICATION_ADDRESS=null
|
||||
BACKUP_ENV=true
|
||||
ALLOW_BACKUP_DELETE=false
|
||||
ALLOW_DATA_PURGE=false
|
||||
ALL_BACKUP_KEEP_DAYS=7
|
||||
DAILY_BACKUP_KEEP_DAYS=16
|
||||
WEEKLY_BACKUP_KEEP_WEEKS=8
|
||||
MONTHLY_BACKUP_KEEP_MONTHS=4
|
||||
YEARLY_BACKUP_KEEP_YEARS=2
|
||||
BACKUP_PURGE_OLDEST_AT_MEGS=5000
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SESSION SETTINGS
|
||||
|
||||
38
.github/ISSUE_TEMPLATE.md
vendored
38
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,38 +0,0 @@
|
||||
#### Expected Behavior (or desired behavior if a feature request)
|
||||
|
||||
(what you expect to happen goes here)
|
||||
|
||||
-----
|
||||
|
||||
#### Actual Behavior
|
||||
|
||||
(what actually happens goes here)
|
||||
|
||||
-----
|
||||
|
||||
#### 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)
|
||||
|
||||
-----
|
||||
#### Provide answers to these questions:
|
||||
|
||||
- Is this a fresh install or an upgrade?
|
||||
- Version of Snipe-IT you're running
|
||||
- Version of PHP you're running
|
||||
- Version of MySQL/MariaDB you're running
|
||||
- What OS and web server you're running Snipe-IT on
|
||||
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
|
||||
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
|
||||
- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the error
|
||||
- If a stacktrace is provided in the error, include that too.
|
||||
- Any errors that appear in your browser's error console.
|
||||
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
|
||||
- Include any additional information you can find in `storage/logs` and your webserver's logs.
|
||||
- Include what you've done so far in the installation, and if you got any error messages along the way.
|
||||
- Indicate whether or not you've manually edited any data directly in the database
|
||||
|
||||
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.
|
||||
|
||||
https://snipe-it.readme.io/docs/getting-help
|
||||
129
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
129
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,129 +0,0 @@
|
||||
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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
25
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
25
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "[Feature Request]: "
|
||||
labels: ["feature request"]
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. The more information you can provide about your use-case, the more liklely we are to consider your feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
40
.github/pull_request_template.md
vendored
40
.github/pull_request_template.md
vendored
@@ -1,40 +0,0 @@
|
||||
# Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
|
||||
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
# How Has This Been Tested?
|
||||
|
||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
|
||||
|
||||
- [ ] Test A
|
||||
- [ ] Test B
|
||||
|
||||
**Test Configuration**:
|
||||
* PHP version:
|
||||
* MySQL version
|
||||
* Webserver version
|
||||
* OS version
|
||||
|
||||
|
||||
# Checklist:
|
||||
|
||||
- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
|
||||
- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
1
.github/travis-memory.ini
vendored
1
.github/travis-memory.ini
vendored
@@ -1 +0,0 @@
|
||||
memory_limit= 2048M
|
||||
7
.github/weekly-digest.yml
vendored
7
.github/weekly-digest.yml
vendored
@@ -1,7 +0,0 @@
|
||||
# Configuration for weekly-digest - https://github.com/apps/weekly-digest
|
||||
publishDay: sun
|
||||
canPublishIssues: true
|
||||
canPublishPullRequests: true
|
||||
canPublishContributors: true
|
||||
canPublishStargazers: true
|
||||
canPublishCommits: true
|
||||
5
.github/workflows/tests-sqlite.yml
vendored
5
.github/workflows/tests-sqlite.yml
vendored
@@ -43,6 +43,9 @@ jobs:
|
||||
cp -v .env.testing.example .env
|
||||
cp -v .env.testing.example .env.testing
|
||||
|
||||
- name: Create database file
|
||||
run: touch database/database.sqlite
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
@@ -57,5 +60,5 @@ jobs:
|
||||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: sqlite_testing
|
||||
DB_CONNECTION: sqlite
|
||||
run: php artisan test
|
||||
|
||||
@@ -52,7 +52,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -73,7 +73,7 @@ RUN mkdir -p /var/www/.composer && chown apache /var/www/.composer
|
||||
|
||||
# Install dependencies
|
||||
USER apache
|
||||
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html
|
||||
RUN COMPOSER_CACHE_DIR=/dev/null composer install --working-dir=/var/www/html
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
74
README.md
74
README.md
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[](#contributing) [](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
@@ -14,6 +14,21 @@ Snipe-IT is actively developed and we [release quite frequently](https://github.
|
||||
> [!TIP]
|
||||
> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
|
||||
|
||||
-----
|
||||
|
||||
### Table of Contents
|
||||
* [Installation](#installation)
|
||||
* [User's Manual](#users-manual)
|
||||
* [Bug Reports & Feature Requests](#bug-reports--feature-requests)
|
||||
* [Security](#security)
|
||||
* [Upgrading](#upgrading)
|
||||
* [Translations!](#translations-)
|
||||
* [Libraries, Modules & Related Projects](#libraries-modules--related-projects)
|
||||
* [Join the Community!](#join-the-community)
|
||||
* [Contributing](#contributing)
|
||||
* [Announcement List](#announcement-list)
|
||||
|
||||
|
||||
-----
|
||||
|
||||
### Installation
|
||||
@@ -22,8 +37,6 @@ 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).
|
||||
@@ -35,20 +48,21 @@ Feel free to check out the [GitHub Issues for this project](https://github.com/s
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
|
||||
>
|
||||
|
||||
-----
|
||||
|
||||
### Security
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**
|
||||
-----
|
||||
|
||||
|
||||
### Upgrading
|
||||
|
||||
Please see the [upgrading documentation](https://snipe-it.readme.io/docs/upgrading) for instructions on upgrading Snipe-IT.
|
||||
|
||||
------
|
||||
### Announcement List
|
||||
|
||||
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
|
||||
|
||||
------
|
||||
|
||||
### Translations!
|
||||
|
||||
Please see the [translations documentation](https://snipe-it.readme.io/docs/translations) for information about available languages and how to add translations to Snipe-IT.
|
||||
@@ -82,23 +96,33 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
|
||||
-----
|
||||
|
||||
### Contributing
|
||||
### Join the Community!
|
||||
|
||||
Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.
|
||||
|
||||
Ideally, contributions should follow from a human-to-human discussion in the form of an issue.
|
||||
|
||||
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
The ERD is available [online here](https://drawsql.app/templates/snipe-it).
|
||||
|
||||
[Here is a list](CONTRIBUTORS.md) of the wonderful people that have contributed to the Snipe-IT.
|
||||
- **[Join our Discord](https://discord.gg/yZFtShAcKk)!** It’s full of great people. We even wrote about it [here](https://grokstar.dev/culture/2024/06/the-unlikely-rise-of-discord-as-a-support-channel/)!
|
||||
- **Follow us on Bluesky** at [@snipeitapp.com](https://bsky.app/profile/snipeitapp.com)
|
||||
- **Follow us on Mastodon** at [hachyderm.io/@grokability](https://hachyderm.io/@grokability)
|
||||
- **Follow our blog** at [Grokstar.Dev](https://grokstar.dev)
|
||||
- **Subscribe here** on Github for notifications about new releases. (We recommend selecting "Releases" only for most users - this repo can get noisy.)
|
||||
|
||||
-----
|
||||
|
||||
### Security
|
||||
### Contributing
|
||||
|
||||
**Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.**
|
||||
|
||||
Contributions should follow from a human-to-human discussion in the form of an issue for the best chances of being merged into the core project. (Sometimes we might already be working on that feature, sometimes we've decided against )
|
||||
|
||||
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
|
||||
This project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
The ERD is available [online here](https://drawsql.app/templates/snipe-it).
|
||||
|
||||
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
|
||||
|
||||
------
|
||||
### Announcement List
|
||||
|
||||
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **To report a security vulnerability, please email security@snipeitapp.com instead of using the issue tracker.**
|
||||
|
||||
@@ -10,10 +10,12 @@ however there are times when library dependencies and/or PHP/MySQL dependencies
|
||||
make it impossible to backport security fixes on older versions.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 5.1.x | :white_check_mark: |
|
||||
|---------| ------------------ |
|
||||
| 7.x | :white_check_mark: |
|
||||
| 6.x | :x: |
|
||||
| 5.1.x | :x: |
|
||||
| 5.0.x | :x: |
|
||||
| 4.0.x | :white_check_mark: |
|
||||
| 4.0.x | :x: |
|
||||
| < 4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
@@ -323,22 +323,29 @@ class LdapSync extends Command
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$add_manager_to_cache = true;
|
||||
if ($ldap_manager["count"] > 0) {
|
||||
try {
|
||||
// Get the Manager's username
|
||||
// PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array.
|
||||
$ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0];
|
||||
|
||||
// Get the Manager's username
|
||||
// PHP LDAP returns every LDAP attribute as an array, and 90% of the time it's an array of just one item. But, hey, it's an array.
|
||||
$ldapManagerUsername = $ldap_manager[0][$ldap_map["username"]][0];
|
||||
// Get User from Manager username.
|
||||
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
|
||||
|
||||
// Get User from Manager username.
|
||||
$ldap_manager = User::where('username', $ldapManagerUsername)->first();
|
||||
|
||||
if ($ldap_manager && isset($ldap_manager->id)) {
|
||||
// Link user to manager id.
|
||||
$user->manager_id = $ldap_manager->id;
|
||||
if ($ldap_manager && isset($ldap_manager->id)) {
|
||||
// Link user to manager id.
|
||||
$user->manager_id = $ldap_manager->id;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$add_manager_to_cache = false;
|
||||
\Log::warning('Handling ldap manager ' . $item['manager'] . ' caused an exception: ' . $e->getMessage() . '. Continuing synchronization.');
|
||||
}
|
||||
}
|
||||
$manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed'
|
||||
if ($add_manager_to_cache) {
|
||||
$manager_cache[$item['manager']] = $ldap_manager && isset($ldap_manager->id) ? $ldap_manager->id : null; // Store results in cache, even if 'failed'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -420,7 +427,13 @@ class LdapSync extends Command
|
||||
$user->groups()->attach($ldap_default_group);
|
||||
}
|
||||
//updates assets location based on user's location
|
||||
Asset::where('assigned_to', '=', $user->id)->update(['location_id' => $user->location_id]);
|
||||
if ($user->wasChanged('location_id')) {
|
||||
foreach ($user->assets as $asset) {
|
||||
$asset->location_id = $user->location_id;
|
||||
// TODO: somehow add note? "Asset Location Changed because of thing"
|
||||
$asset->save();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
|
||||
@@ -59,7 +59,7 @@ class ObjectImportCommand extends Command
|
||||
$classString = "App\\Importer\\{$class}Importer";
|
||||
$importer = new $classString($filename);
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId($this->option('user_id'))
|
||||
->setCreatedBy($this->option('user_id'))
|
||||
->setUpdating($this->option('update'))
|
||||
->setShouldNotify($this->option('send-welcome'))
|
||||
->setUsernameFormat($this->option('username_format'));
|
||||
|
||||
@@ -50,12 +50,12 @@ class ResetDemoSettings extends Command
|
||||
$settings->alert_email = 'service@snipe-it.io';
|
||||
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
|
||||
$settings->header_color = null;
|
||||
$settings->barcode_type = 'QRCODE';
|
||||
$settings->label2_2d_type = 'QRCODE';
|
||||
$settings->default_currency = 'USD';
|
||||
$settings->brand = 2;
|
||||
$settings->ldap_enabled = 0;
|
||||
$settings->full_multiple_companies_support = 0;
|
||||
$settings->alt_barcode = 'C128';
|
||||
$settings->label2_1d_type = 'C128';
|
||||
$settings->skin = '';
|
||||
$settings->email_domain = 'snipeitapp.com';
|
||||
$settings->email_format = 'filastname';
|
||||
@@ -65,7 +65,7 @@ class ResetDemoSettings extends Command
|
||||
$settings->thumbnail_max_h = '30';
|
||||
$settings->locale = 'en-US';
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = null;
|
||||
$settings->support_footer = 'on';
|
||||
$settings->saml_enabled = '0';
|
||||
$settings->saml_sp_x509cert = null;
|
||||
$settings->saml_idp_metadata = null;
|
||||
|
||||
@@ -51,6 +51,8 @@ class SQLStreamer {
|
||||
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
|
||||
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
|
||||
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
|
||||
"<^/\*!40101 SET NAMES '?[a-zA-Z0-9_-]+'? \*/;$>" => false, //using weird delimiters (<,>) for readability. allow quoted or unquoted charsets
|
||||
"<^/\*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' \*/;$>" => false, //same, now handle zero-values
|
||||
];
|
||||
|
||||
foreach($allowed_statements as $statement => $statechange) {
|
||||
@@ -370,7 +372,7 @@ class RestoreFromBackup extends Command
|
||||
if ($this->option('sanitize-guess-prefix')) {
|
||||
$prefix = SQLStreamer::guess_prefix($sql_contents);
|
||||
$this->line($prefix);
|
||||
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
|
||||
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
|
||||
}
|
||||
|
||||
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Mail\UnacceptedAssetReminderMail;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use App\Notifications\UnacceptedAssetReminderNotification;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendAcceptanceReminder extends Command
|
||||
{
|
||||
@@ -65,42 +65,29 @@ class SendAcceptanceReminder extends Command
|
||||
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
|
||||
});
|
||||
|
||||
$no_mail_address = [];
|
||||
|
||||
foreach($unacceptedAssetGroups as $unacceptedAssetGroup) {
|
||||
// The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user.
|
||||
// Collapsing and flattening the collection doesn't work above.
|
||||
$acceptance = $unacceptedAssetGroup[0]['acceptance'];
|
||||
$locale = $acceptance->assignedTo?->locale;
|
||||
$email = $acceptance->assignedTo?->email;
|
||||
if(!$email){
|
||||
$this->info($acceptance->assignedTo?->present()->fullName().' has no email address.');
|
||||
}
|
||||
$item_count = $unacceptedAssetGroup->count();
|
||||
foreach ($unacceptedAssetGroup as $unacceptedAsset) {
|
||||
// if ($unacceptedAsset['acceptance']->assignedTo->email == ''){
|
||||
// $no_mail_address[] = $unacceptedAsset['checkoutable']->assignedTo->present()->fullName;
|
||||
// }
|
||||
if ($unacceptedAsset['acceptance']->assignedTo) {
|
||||
|
||||
if (!$unacceptedAsset['acceptance']->assignedTo->locale) {
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$unacceptedAsset['acceptance']->assignedTo,
|
||||
new UnacceptedAssetReminderNotification($unacceptedAsset['assetItem'], $count)
|
||||
);
|
||||
} else {
|
||||
Notification::send(
|
||||
$unacceptedAsset['acceptance']->assignedTo,
|
||||
new UnacceptedAssetReminderNotification($unacceptedAsset, $item_count)
|
||||
);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
if ($locale && $email) {
|
||||
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale));
|
||||
|
||||
} elseif ($email) {
|
||||
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count)));
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
if (!empty($no_mail_address)) {
|
||||
foreach($no_mail_address as $user) {
|
||||
return $user.' has no email.';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->info($count.' users notified.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
28
app/Events/NoteAdded.php
Normal file
28
app/Events/NoteAdded.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class NoteAdded
|
||||
{
|
||||
use Dispatchable, SerializesModels;
|
||||
public $itemNoteAddedOn;
|
||||
public $note;
|
||||
public $noteAddedBy;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($itemNoteAddedOn, User $noteAddedBy, $note)
|
||||
{
|
||||
$this->itemNoteAddedOn = $itemNoteAddedOn;
|
||||
$this->note = $note;
|
||||
$this->noteAddedBy = $noteAddedBy;
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,9 @@ class IconHelper
|
||||
return 'fa-regular fa-id-card';
|
||||
case 'department' :
|
||||
return 'fa-solid fa-building-user';
|
||||
|
||||
case 'note':
|
||||
case 'notes':
|
||||
return 'fas fa-sticky-note';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class AccessoriesController extends Controller
|
||||
public function index() : View
|
||||
{
|
||||
$this->authorize('index', Accessory::class);
|
||||
return view('accessories/index');
|
||||
return view('accessories.index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,7 @@ class AccessoriesController extends Controller
|
||||
|
||||
if ($item = Accessory::find($accessoryId)) {
|
||||
$this->authorize($item);
|
||||
return view('accessories/edit', compact('item'))->with('category_type', 'accessory');
|
||||
return view('accessories.edit', compact('item'))->with('category_type', 'accessory');
|
||||
}
|
||||
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
@@ -236,7 +236,7 @@ class AccessoriesController extends Controller
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
|
||||
$this->authorize('view', $accessory);
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
return view('accessories.view', compact('accessory'));
|
||||
}
|
||||
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist', ['id' => $accessoryID]));
|
||||
|
||||
@@ -51,7 +51,7 @@ class AccessoriesFilesController extends Controller
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('accessories.show', $accessory->id)->with('success', trans('general.file_upload_success'));
|
||||
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
|
||||
|
||||
}
|
||||
|
||||
@@ -90,8 +90,7 @@ class AccessoriesFilesController extends Controller
|
||||
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
// Redirect to the licence management page
|
||||
|
||||
@@ -75,20 +75,23 @@ class AccessoryCheckoutController extends Controller
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
AccessoryCheckout::create([
|
||||
|
||||
$accessory_checkout = new AccessoryCheckout([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'created_by' => auth()->id(),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
$accessory_checkout->created_by = auth()->id();
|
||||
$accessory_checkout->save();
|
||||
}
|
||||
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
|
||||
// Set this as user since we only allow checkout to user for this item type
|
||||
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||
$request->request->add(['assigned_user' => $target->id]);
|
||||
$request->request->add(['assigned_to' => $target->id]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
|
||||
@@ -40,15 +40,16 @@ class ActionlogController extends Controller
|
||||
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
$file = config('app.private_uploads').'/eula-pdfs/'.$filename;
|
||||
|
||||
if (Storage::exists($file)) {
|
||||
return response()->download($file);
|
||||
if (config('filesystems.default') == 's3_private') {
|
||||
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
|
||||
}
|
||||
|
||||
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
|
||||
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -184,39 +185,33 @@ class AccessoriesController extends Controller
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
* Get the list of checkouts for a specific accessory
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return | array
|
||||
*/
|
||||
public function checkedout($id, Request $request)
|
||||
public function checkedout(Request $request, $id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
|
||||
$accessory = Accessory::with('lastCheckout')->findOrFail($id);
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts;
|
||||
$total = $accessory_checkouts->count();
|
||||
|
||||
if ($total < $offset) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
|
||||
// Total count of all checkouts for this asset
|
||||
$accessory_checkouts = $accessory->checkouts();
|
||||
|
||||
// Check for search text in the request
|
||||
if ($request->filled('search')) {
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
|
||||
->get();
|
||||
$total = $accessory_checkouts->count();
|
||||
$accessory_checkouts = $accessory_checkouts->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
|
||||
$total = $accessory_checkouts->count();
|
||||
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
|
||||
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_checkouts, $total);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +222,7 @@ class AccessoriesController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
@@ -249,7 +244,7 @@ class AccessoriesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
@@ -284,14 +279,17 @@ class AccessoriesController extends Controller
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
AccessoryCheckout::create([
|
||||
|
||||
$accessory_checkout = new AccessoryCheckout([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'created_by' => auth()->id(),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
|
||||
$accessory_checkout->created_by = auth()->id();
|
||||
$accessory_checkout->save();
|
||||
}
|
||||
|
||||
// Set this value to be able to pass the qty through to the event
|
||||
|
||||
@@ -122,7 +122,7 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function store(Request $request) : JsonResponse
|
||||
public function store(Request $request) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
@@ -149,7 +149,7 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function update(Request $request, $id) : JsonResponse
|
||||
public function update(Request $request, $id) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
@@ -186,7 +186,7 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function destroy($assetMaintenanceId) : JsonResponse
|
||||
public function destroy($assetMaintenanceId) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
@@ -208,7 +208,7 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function show($assetMaintenanceId) : JsonResponse
|
||||
public function show($assetMaintenanceId) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
@@ -68,37 +69,15 @@ class AssetModelFilesController extends Controller
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $assetmodel
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function list($assetModelId = null) : JsonResponse
|
||||
public function list($assetmodel_id) : JsonResponse | array
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that there are some uploads on this asset that can be listed
|
||||
if ($assetModel->uploads->count() > 0) {
|
||||
$files = array();
|
||||
foreach ($assetModel->uploads as $upload) {
|
||||
array_push($files, $upload);
|
||||
}
|
||||
// Give the list of files back to the user
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// There are no files.
|
||||
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error')), 500);
|
||||
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
|
||||
$this->authorize('view', $assetmodel);
|
||||
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,17 +56,17 @@ class AssetModelsController extends Controller
|
||||
'models.id',
|
||||
'models.image',
|
||||
'models.name',
|
||||
'model_number',
|
||||
'min_amt',
|
||||
'eol',
|
||||
'created_by',
|
||||
'requestable',
|
||||
'models.model_number',
|
||||
'models.min_amt',
|
||||
'models.eol',
|
||||
'models.created_by',
|
||||
'models.requestable',
|
||||
'models.notes',
|
||||
'models.created_at',
|
||||
'category_id',
|
||||
'manufacturer_id',
|
||||
'depreciation_id',
|
||||
'fieldset_id',
|
||||
'models.category_id',
|
||||
'models.manufacturer_id',
|
||||
'models.depreciation_id',
|
||||
'models.fieldset_id',
|
||||
'models.deleted_at',
|
||||
'models.updated_at',
|
||||
])
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||
use App\Http\Requests\StoreAssetRequest;
|
||||
use App\Http\Requests\UpdateAssetRequest;
|
||||
use App\Http\Traits\MigratesLegacyAssetLocations;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\LicenseSeat;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -26,13 +27,13 @@ use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\View\Label;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
/**
|
||||
@@ -80,10 +81,10 @@ class AssetsController extends Controller
|
||||
$this->authorize('reports.view');
|
||||
} else {
|
||||
$transformer = 'App\Http\Transformers\AssetsTransformer';
|
||||
$this->authorize('index', Asset::class);
|
||||
$this->authorize('index', Asset::class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$allowed_columns = [
|
||||
@@ -126,8 +127,20 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo', 'adminuser','model.depreciation',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
||||
->with(
|
||||
'model',
|
||||
'location',
|
||||
'assetstatus',
|
||||
'company',
|
||||
'defaultLoc',
|
||||
'assignedTo',
|
||||
'adminuser',
|
||||
'model.depreciation',
|
||||
'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) {
|
||||
@@ -159,8 +172,8 @@ class AssetsController extends Controller
|
||||
* Handle due and overdue audits and checkin dates
|
||||
*/
|
||||
switch ($action) {
|
||||
// Audit (singular) is left over from earlier legacy APIs
|
||||
case 'audits' :
|
||||
// Audit (singular) is left over from earlier legacy APIs
|
||||
case 'audits':
|
||||
switch ($upcoming_status) {
|
||||
case 'due':
|
||||
$assets->DueForAudit($settings);
|
||||
@@ -187,7 +200,7 @@ class AssetsController extends Controller
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End handling due and overdue audits and checkin dates
|
||||
@@ -265,7 +278,6 @@ class AssetsController extends Controller
|
||||
$join->on('status_alias.id', '=', 'assets.status_id');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -345,7 +357,7 @@ class AssetsController extends Controller
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
@@ -399,7 +411,6 @@ class AssetsController extends Controller
|
||||
} else {
|
||||
$assets->orderBy($sort_override, $order);
|
||||
}
|
||||
|
||||
} else {
|
||||
$assets->orderBy($column_sort, $order);
|
||||
}
|
||||
@@ -413,11 +424,11 @@ class AssetsController extends Controller
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Include additional associated relationships
|
||||
*/
|
||||
*/
|
||||
if ($request->input('components')) {
|
||||
$assets->loadMissing(['components' => function ($query) {
|
||||
$query->orderBy('created_at', 'desc');
|
||||
@@ -441,7 +452,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.2.1]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function showByTag(Request $request, $tag) : JsonResponse | array
|
||||
public function showByTag(Request $request, $tag): JsonResponse | array
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('asset_tag', $tag)->with('assetstatus')->with('assignedTo');
|
||||
@@ -463,12 +474,10 @@ class AssetsController extends Controller
|
||||
} else {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If there are 0 results, return the "no such asset" response
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,7 +488,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.2.1]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function showBySerial(Request $request, $serial) : JsonResponse | array
|
||||
public function showBySerial(Request $request, $serial): JsonResponse | array
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo');
|
||||
@@ -488,14 +497,13 @@ class AssetsController extends Controller
|
||||
if ($request->input('deleted', 'false') == 'true') {
|
||||
$assets = $assets->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
if (($assets = $assets->get()) && ($assets->count()) > 0) {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
// If there are 0 results, return the "no such asset" response
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -506,20 +514,20 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, $id) : JsonResponse | array
|
||||
public function show(Request $request, $id): JsonResponse | array
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')
|
||||
->with('assignedTo')->withTrashed()
|
||||
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)) {
|
||||
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)
|
||||
) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request->input('components') );
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request->input('components'));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
public function licenses(Request $request, $id) : array
|
||||
public function licenses(Request $request, $id): array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', License::class);
|
||||
@@ -527,7 +535,7 @@ class AssetsController extends Controller
|
||||
$licenses = $asset->licenses()->get();
|
||||
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -537,7 +545,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*/
|
||||
public function selectlist(Request $request) : array
|
||||
public function selectlist(Request $request): array
|
||||
{
|
||||
|
||||
$assets = Asset::select([
|
||||
@@ -548,7 +556,7 @@ class AssetsController extends Controller
|
||||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id',
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||
|
||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
@@ -570,12 +578,12 @@ class AssetsController extends Controller
|
||||
$asset->use_text = $asset->present()->fullName;
|
||||
|
||||
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
$asset->use_text .= ' → ' . $asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
$asset->use_text .= '(' . $asset->assetstatus->getStatuslabelType() . ')';
|
||||
}
|
||||
|
||||
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
|
||||
@@ -601,12 +609,12 @@ class AssetsController extends Controller
|
||||
$asset->created_by = auth()->id();
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
* 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);
|
||||
|
||||
@@ -623,9 +631,9 @@ class AssetsController extends Controller
|
||||
|
||||
// If input value is null, use custom field's default value
|
||||
if ($field_val == null) {
|
||||
Log::debug('Field value for '.$field->db_column.' is null');
|
||||
Log::debug('Field value for ' . $field->db_column . ' is null');
|
||||
$field_val = $field->defaultValue($request->get('model_id'));
|
||||
Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
|
||||
Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id')));
|
||||
}
|
||||
|
||||
// if the field is set to encrypted, make sure we encrypt the value
|
||||
@@ -643,7 +651,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
if ($field->element == 'checkbox') {
|
||||
if(is_array($field_val)) {
|
||||
if (is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
}
|
||||
@@ -702,64 +710,64 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
* 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);
|
||||
$model = $asset->model;
|
||||
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$field_val = $request->input($field->db_column, null);
|
||||
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->element == 'checkbox') {
|
||||
if(is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$field_val = $request->input($field->db_column, null);
|
||||
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->element == 'checkbox') {
|
||||
if (is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
$field_val = Crypt::encrypt($field_val);
|
||||
} else {
|
||||
$problems_updating_encrypted_custom_fields = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$asset->{$field->db_column} = $field_val;
|
||||
}
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
$field_val = Crypt::encrypt($field_val);
|
||||
} else {
|
||||
$problems_updating_encrypted_custom_fields = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$asset->{$field->db_column} = $field_val;
|
||||
}
|
||||
}
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
}
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
|
||||
}
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
|
||||
}
|
||||
|
||||
if ($asset->image) {
|
||||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
if ($asset->image) {
|
||||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning')));
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
|
||||
}
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
|
||||
@@ -773,7 +781,7 @@ class AssetsController extends Controller
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function destroy($id) : JsonResponse
|
||||
public function destroy($id): JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
@@ -799,7 +807,7 @@ class AssetsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Restore a soft-deleted asset.
|
||||
@@ -808,7 +816,7 @@ class AssetsController extends Controller
|
||||
* @param int $assetId
|
||||
* @since [v5.1.18]
|
||||
*/
|
||||
public function restore(Request $request, $assetId = null) : JsonResponse
|
||||
public function restore(Request $request, $assetId = null): JsonResponse
|
||||
{
|
||||
|
||||
if ($asset = Asset::withTrashed()->find($assetId)) {
|
||||
@@ -827,7 +835,6 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -837,7 +844,7 @@ class AssetsController extends Controller
|
||||
* @param string $tag
|
||||
* @since [v6.0.5]
|
||||
*/
|
||||
public function checkoutByTag(AssetCheckoutRequest $request, $tag) : JsonResponse
|
||||
public function checkoutByTag(AssetCheckoutRequest $request, $tag): JsonResponse
|
||||
{
|
||||
if ($asset = Asset::where('asset_tag', $tag)->first()) {
|
||||
return $this->checkout($request, $asset->id);
|
||||
@@ -852,13 +859,13 @@ class AssetsController extends Controller
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id) : JsonResponse
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id): JsonResponse
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
|
||||
if (! $asset->availableForCheckout()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
@@ -875,14 +882,12 @@ class AssetsController extends Controller
|
||||
$asset->location_id = ($target) ? $target->id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_location');
|
||||
$error_payload['target_type'] = 'location';
|
||||
|
||||
} elseif (request('checkout_to_type') == 'asset') {
|
||||
$target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset'));
|
||||
// Override with the asset's location_id if it has one
|
||||
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_asset');
|
||||
$error_payload['target_type'] = 'asset';
|
||||
|
||||
} elseif (request('checkout_to_type') == 'user') {
|
||||
// Fetch the target and set the asset's new location_id
|
||||
$target = User::find(request('assigned_user'));
|
||||
@@ -896,7 +901,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if (! isset($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset ' . e($asset->asset_tag) . ' is invalid - ' . $error_payload['target_type'] . ' does not exist.'));
|
||||
}
|
||||
|
||||
$checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
|
||||
@@ -910,15 +915,15 @@ class AssetsController extends Controller
|
||||
// TODO: Follow up here. WTF. Commented out for now.
|
||||
|
||||
|
||||
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
|
||||
// $asset->location_id = $target->rtd_location_id;
|
||||
// }
|
||||
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
|
||||
// $asset->location_id = $target->rtd_location_id;
|
||||
// }
|
||||
|
||||
if ($asset->checkOut($target, auth()->user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
|
||||
}
|
||||
|
||||
|
||||
@@ -929,7 +934,7 @@ class AssetsController extends Controller
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function checkin(Request $request, $asset_id) : JsonResponse
|
||||
public function checkin(Request $request, $asset_id): JsonResponse
|
||||
{
|
||||
$asset = Asset::with('model')->findOrFail($asset_id);
|
||||
$this->authorize('checkin', $asset);
|
||||
@@ -937,7 +942,7 @@ class AssetsController extends Controller
|
||||
$target = $asset->assignedTo;
|
||||
if (is_null($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.already_checked_in')));
|
||||
@@ -960,7 +965,7 @@ class AssetsController extends Controller
|
||||
if ($request->filled('location_id')) {
|
||||
$asset->location_id = $request->input('location_id');
|
||||
|
||||
if ($request->input('update_default_location')){
|
||||
if ($request->input('update_default_location')) {
|
||||
$asset->rtd_location_id = $request->input('location_id');
|
||||
}
|
||||
}
|
||||
@@ -968,8 +973,8 @@ class AssetsController extends Controller
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->input('status_id');
|
||||
}
|
||||
|
||||
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at').' '. date('H:i:s') : date('Y-m-d H:i:s');
|
||||
|
||||
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at') . ' ' . date('H:i:s') : date('Y-m-d H:i:s');
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
|
||||
@@ -987,7 +992,8 @@ class AssetsController extends Controller
|
||||
[Asset::class],
|
||||
function (Builder $query) use ($asset) {
|
||||
$query->where('id', $asset->id);
|
||||
})
|
||||
}
|
||||
)
|
||||
->get()
|
||||
->map(function ($acceptance) {
|
||||
$acceptance->delete();
|
||||
@@ -997,13 +1003,13 @@ class AssetsController extends Controller
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['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')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1012,7 +1018,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Janes] [<ajanes@adagiohealth.org>]
|
||||
* @since [v6.0]
|
||||
*/
|
||||
public function checkinByTag(Request $request, $tag = null) : JsonResponse
|
||||
public function checkinByTag(Request $request, $tag = null): JsonResponse
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
if (null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
@@ -1025,8 +1031,8 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset'=> e($tag)
|
||||
], 'Asset with tag '.e($tag).' not found'));
|
||||
'asset' => e($tag)
|
||||
], 'Asset with tag ' . e($tag) . ' not found'));
|
||||
}
|
||||
|
||||
|
||||
@@ -1037,7 +1043,7 @@ class AssetsController extends Controller
|
||||
* @param int $id
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function audit(Request $request) : JsonResponse
|
||||
public function audit(Request $request): JsonResponse
|
||||
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
@@ -1048,8 +1054,8 @@ class AssetsController extends Controller
|
||||
// No tag passed - return an error
|
||||
if (!$request->filled('asset_tag')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> '',
|
||||
'error'=> trans('admin/hardware/message.no_tag'),
|
||||
'asset_tag' => '',
|
||||
'error' => trans('admin/hardware/message.no_tag'),
|
||||
], trans('admin/hardware/message.no_tag')), 200);
|
||||
}
|
||||
|
||||
@@ -1097,28 +1103,25 @@ class AssetsController extends Controller
|
||||
$asset->logAudit(request('note'), request('location_id'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'note'=> e($request->input('note')),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'note' => e($request->input('note')),
|
||||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
|
||||
], trans('admin/hardware/message.audit.success')));
|
||||
}
|
||||
|
||||
// Asset failed validation or was not able to be saved
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'error'=> $asset->getErrors()->first(),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'error' => $asset->getErrors()->first(),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// No matching asset for the asset tag that was passed.
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($request->input('asset_tag')),
|
||||
'error'=> trans('admin/hardware/message.audit.error'),
|
||||
'asset_tag' => e($request->input('asset_tag')),
|
||||
'error' => trans('admin/hardware/message.audit.error'),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1129,7 +1132,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function requestable(Request $request) : JsonResponse | array
|
||||
public function requestable(Request $request): JsonResponse | array
|
||||
{
|
||||
$this->authorize('viewRequestable', Asset::class);
|
||||
|
||||
@@ -1150,8 +1153,18 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'assetlog', 'company','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests');
|
||||
->with(
|
||||
'location',
|
||||
'assetstatus',
|
||||
'assetlog',
|
||||
'company',
|
||||
'assignedTo',
|
||||
'model.category',
|
||||
'model.manufacturer',
|
||||
'model.fieldset',
|
||||
'supplier',
|
||||
'requests'
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -1159,7 +1172,7 @@ class AssetsController extends Controller
|
||||
if ($request->filled('search')) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name())) {
|
||||
@@ -1200,4 +1213,110 @@ class AssetsController extends Controller
|
||||
|
||||
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
|
||||
}
|
||||
|
||||
|
||||
public function assignedAssets(Request $request, Asset $asset) : JsonResponse | array
|
||||
{
|
||||
|
||||
return [];
|
||||
// to do
|
||||
}
|
||||
|
||||
public function assignedAccessories(Request $request, Asset $asset) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', $asset);
|
||||
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()->with('adminuser')->with('accessories');
|
||||
|
||||
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$total = $accessory_checkouts->count();
|
||||
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
|
||||
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
|
||||
}
|
||||
/**
|
||||
* Generate asset labels by tag
|
||||
*
|
||||
* @author [Nebelkreis] [https://github.com/NebelKreis]
|
||||
*
|
||||
* @param Request $request Contains asset_tags array of asset tags to generate labels for
|
||||
* @return JsonResponse Returns base64 encoded PDF on success, error message on failure
|
||||
*/
|
||||
public function getLabels(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
// Validate that asset tags were provided in the request
|
||||
if (!$request->filled('asset_tags')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null,
|
||||
trans('admin/hardware/message.no_assets_selected')), 400);
|
||||
}
|
||||
|
||||
// Convert asset tags from request into collection and fetch matching assets
|
||||
$asset_tags = collect($request->input('asset_tags'));
|
||||
$assets = Asset::whereIn('asset_tag', $asset_tags)->get();
|
||||
|
||||
// Return error if no assets were found for the provided tags
|
||||
if ($assets->isEmpty()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null,
|
||||
trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
try {
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
// Check if logo file exists in storage and disable logo if not found
|
||||
// This prevents errors when trying to include a non-existent logo in the PDF
|
||||
$settings->label_logo = ($original_logo = $settings->label_logo) && !Storage::disk('public')->exists('/' . $original_logo) ? null : $settings->label_logo;
|
||||
|
||||
|
||||
$label = new Label();
|
||||
|
||||
if (!$label) {
|
||||
throw new \Exception('Label object could not be created');
|
||||
}
|
||||
|
||||
// Configure label with assets and settings
|
||||
// bulkedit=false and count=0 are default values for label generation
|
||||
$label = $label->with('assets', $assets)
|
||||
->with('settings', $settings)
|
||||
->with('bulkedit', false)
|
||||
->with('count', 0);
|
||||
|
||||
// Generate PDF using callback function
|
||||
// The callback captures the PDF content in $pdf_content variable
|
||||
$pdf_content = '';
|
||||
$label->render(function($pdf) use (&$pdf_content) {
|
||||
$pdf_content = $pdf->Output('', 'S');
|
||||
return $pdf;
|
||||
});
|
||||
|
||||
// Verify PDF was generated successfully
|
||||
if (empty($pdf_content)) {
|
||||
throw new \Exception('PDF content is empty');
|
||||
}
|
||||
|
||||
$encoded_content = base64_encode($pdf_content);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'pdf' => $encoded_content
|
||||
], trans('admin/hardware/message.labels_generated')));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'error_message' => $e->getMessage(),
|
||||
'error_line' => $e->getLine(),
|
||||
'error_file' => $e->getFile()
|
||||
], trans('admin/hardware/message.error_generating_labels')), 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'error_message' => $e->getMessage(),
|
||||
'error_line' => $e->getLine(),
|
||||
'error_file' => $e->getFile()
|
||||
], $e->getMessage()), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class CompaniesController extends Controller
|
||||
|
||||
$companies = Company::withCount(['assets as assets_count' => function ($query) {
|
||||
$query->AssetsForShow();
|
||||
}])->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
|
||||
}])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies->TextSearch($request->input('search'));
|
||||
@@ -62,10 +62,11 @@ class CompaniesController extends Controller
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value');
|
||||
$offset = ($request->input('offset') > $companies->count()) ? 0 : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort_override = $request->input('sort');
|
||||
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'created_at';
|
||||
|
||||
switch ($sort_override) {
|
||||
|
||||
@@ -309,9 +309,7 @@ class ComponentsController extends Controller
|
||||
public function checkin(Request $request, $component_asset_id) : JsonResponse
|
||||
{
|
||||
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')));
|
||||
}
|
||||
|
||||
@@ -319,17 +317,13 @@ class ComponentsController extends Controller
|
||||
|
||||
$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));
|
||||
}
|
||||
$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));
|
||||
@@ -339,28 +333,23 @@ class ComponentsController extends Controller
|
||||
$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]);
|
||||
|
||||
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) {
|
||||
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'));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class ConsumablesController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$consumables->where('company_id', '=', $request->input('company_id'));
|
||||
$consumables->where('consumables.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('category_id')) {
|
||||
|
||||
@@ -72,6 +72,9 @@ class DepartmentsController extends Controller
|
||||
case 'manager':
|
||||
$departments->OrderManager($order);
|
||||
break;
|
||||
case 'company':
|
||||
$departments->OrderCompany($order);
|
||||
break;
|
||||
default:
|
||||
$departments->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -9,12 +9,14 @@ use App\Http\Transformers\ImportsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Database\Eloquent\JsonEncodingException;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Csv\Reader;
|
||||
use Onnov\DetectEncoding\EncodingDetector;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -45,6 +47,8 @@ class ImportController extends Controller
|
||||
$path = config('app.private_uploads').'/imports';
|
||||
$results = [];
|
||||
$import = new Import;
|
||||
$detector = new EncodingDetector();
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (! in_array($file->getMimeType(), [
|
||||
'application/vnd.ms-excel',
|
||||
@@ -55,7 +59,6 @@ class ImportController extends Controller
|
||||
'text/comma-separated-values',
|
||||
'text/tsv', ])) {
|
||||
$results['error'] = 'File type must be CSV. Uploaded file is '.$file->getMimeType();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 422);
|
||||
}
|
||||
|
||||
@@ -63,7 +66,25 @@ class ImportController extends Controller
|
||||
if (! ini_get('auto_detect_line_endings')) {
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
}
|
||||
$file_contents = $file->getContent(); //TODO - this *does* load the whole file in RAM, but we need that to be able to 'iconv' it?
|
||||
$encoding = $detector->getEncoding($file_contents);
|
||||
$reader = null;
|
||||
if (strcasecmp($encoding, 'UTF-8') != 0) {
|
||||
$transliterated = iconv($encoding, 'UTF-8', $file_contents);
|
||||
if ($transliterated !== false) {
|
||||
$tmpname = tempnam(sys_get_temp_dir(), '');
|
||||
$tmpresults = file_put_contents($tmpname, $transliterated);
|
||||
if ($tmpresults !== false) {
|
||||
$transliterated = null; //save on memory?
|
||||
$newfile = new UploadedFile($tmpname, $file->getClientOriginalName(), null, null, true); //WARNING: this is enabling 'test mode' - which is gross, but otherwise the file won't be treated as 'uploaded'
|
||||
if ($newfile->isValid()) {
|
||||
$file = $newfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
|
||||
$file_contents = null; //try to save on memory, I guess?
|
||||
|
||||
try {
|
||||
$import->header_row = $reader->fetchOne(0);
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\LocationsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class LocationsController extends Controller
|
||||
{
|
||||
@@ -28,26 +31,28 @@ class LocationsController extends Controller
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'accessories_count',
|
||||
'address',
|
||||
'address2',
|
||||
'assets_count',
|
||||
'assets_count',
|
||||
'assigned_accessories_count',
|
||||
'assigned_assets_count',
|
||||
'assigned_assets_count',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'manager_id',
|
||||
'image',
|
||||
'assigned_assets_count',
|
||||
'users_count',
|
||||
'assets_count',
|
||||
'assigned_assets_count',
|
||||
'assets_count',
|
||||
'rtd_assets_count',
|
||||
'currency',
|
||||
'id',
|
||||
'image',
|
||||
'ldap_ou',
|
||||
'manager_id',
|
||||
'name',
|
||||
'rtd_assets_count',
|
||||
'state',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
'zip',
|
||||
];
|
||||
|
||||
$locations = Location::with('parent', 'manager', 'children')->select([
|
||||
@@ -68,8 +73,11 @@ class LocationsController extends Controller
|
||||
'locations.image',
|
||||
'locations.ldap_ou',
|
||||
'locations.currency',
|
||||
])->withCount('assignedAssets as assigned_assets_count')
|
||||
])
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('assignedAccessories as assigned_accessories_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count');
|
||||
@@ -224,7 +232,17 @@ class LocationsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
public function assets(Request $request, Location $location) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', $location);
|
||||
$assets = Asset::where('location_id', '=', $location->id)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
|
||||
$assets = $assets->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
|
||||
public function assignedAssets(Request $request, Location $location) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', $location);
|
||||
@@ -233,6 +251,20 @@ class LocationsController extends Controller
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
|
||||
public function assignedAccessories(Request $request, Location $location) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$this->authorize('view', $location);
|
||||
$accessory_checkouts = AccessoryCheckout::LocationAssigned()->with('adminuser')->with('accessories');
|
||||
|
||||
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$total = $accessory_checkouts->count();
|
||||
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
|
||||
return (new LocationsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
||||
43
app/Http/Controllers/Api/NotesController.php
Normal file
43
app/Http/Controllers/Api/NotesController.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\NoteAdded;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class NotesController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'note' => 'required|string|max:500',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['asset']),
|
||||
],
|
||||
]);
|
||||
|
||||
// This can be made dynamic by using $request->input('type') to determine which model type to add the note to.
|
||||
// For now, we are only placing this on Assets
|
||||
$item = Asset::findOrFail($request->input("id"));
|
||||
$this->authorize('update', $item);
|
||||
|
||||
event(new NoteAdded($item, Auth::user(), $validated['note']));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success'));
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
|
||||
}
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,25 @@ class ReportsController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('action_type')) {
|
||||
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'));
|
||||
}
|
||||
|
||||
if ($request->filled('created_by')) {
|
||||
$actionlogs = $actionlogs->where('created_by', '=', $request->input('created_by'));
|
||||
}
|
||||
|
||||
if ($request->filled('action_source')) {
|
||||
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'));
|
||||
}
|
||||
|
||||
if ($request->filled('remote_ip')) {
|
||||
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('uploads')) {
|
||||
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
|
||||
$actionlogs = $actionlogs->whereNotNull('filename');
|
||||
}
|
||||
|
||||
$allowed_columns = [
|
||||
@@ -78,7 +94,7 @@ class ReportsController extends Controller
|
||||
$actionlogs->OrderByCreatedBy($order);
|
||||
break;
|
||||
default:
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'action_logs.created_at';
|
||||
$actionlogs = $actionlogs->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use App\Models\License;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -46,7 +47,6 @@ class UsersController extends Controller
|
||||
'users.address',
|
||||
'users.avatar',
|
||||
'users.city',
|
||||
'users.company_id',
|
||||
'users.country',
|
||||
'users.created_by',
|
||||
'users.created_at',
|
||||
@@ -79,8 +79,17 @@ class UsersController extends Controller
|
||||
'users.autoassign_licenses',
|
||||
'users.website',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
|
||||
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'managesUsers as manages_users_count', 'managedLocations as manages_locations_count');
|
||||
])->with('groups', 'userloc', 'companies', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations', 'manager')
|
||||
->withCount([
|
||||
'assets as assets_count' => function(Builder $query) {
|
||||
$query->withoutTrashed();
|
||||
},
|
||||
'licenses as licenses_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'managesUsers as manages_users_count',
|
||||
'managedLocations as manages_locations_count'
|
||||
]);
|
||||
|
||||
|
||||
if ($request->filled('search') != '') {
|
||||
@@ -92,7 +101,7 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$users = $users->where('users.company_id', '=', $request->input('company_id'));
|
||||
$users = $users->ByCompany($request->get('company_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('location_id')) {
|
||||
@@ -233,9 +242,9 @@ class UsersController extends Controller
|
||||
case 'created_by':
|
||||
$users = $users->OrderByCreatedBy($order);
|
||||
break;
|
||||
case 'company':
|
||||
$users = $users->OrderCompany($order);
|
||||
break;
|
||||
// case 'company':
|
||||
// $users = $users->OrderCompany($order);
|
||||
// break;
|
||||
case 'first_name':
|
||||
$users->orderBy('first_name', $order);
|
||||
$users->orderBy('last_name', $order);
|
||||
@@ -283,6 +292,7 @@ class UsersController extends Controller
|
||||
'autoassign_licenses',
|
||||
'website',
|
||||
'locale',
|
||||
'notes',
|
||||
];
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
|
||||
@@ -401,6 +411,8 @@ class UsersController extends Controller
|
||||
$user->groups()->sync([]);
|
||||
}
|
||||
|
||||
$user->companies()->sync($request->input('companies'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
|
||||
}
|
||||
|
||||
@@ -480,10 +492,11 @@ class UsersController extends Controller
|
||||
$user->permissions = $permissions_array;
|
||||
}
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
|
||||
|
||||
if($request->has('location_id')) {
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
|
||||
}
|
||||
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
|
||||
|
||||
if ($user->save()) {
|
||||
@@ -500,6 +513,7 @@ class UsersController extends Controller
|
||||
|
||||
// Sync the groups since the user is a superuser and the groups pass validation
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
$user->companies()->sync($request->input('companies'));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.update')));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ use \Illuminate\Http\RedirectResponse;
|
||||
*/
|
||||
class AssetModelsController extends Controller
|
||||
{
|
||||
protected MessageBag $validatorErrors;
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the accessories listing, which is generated in getDatatable.
|
||||
@@ -158,7 +160,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
|
||||
return redirect()->back()->withInput()->withErrors($this->validatorErrors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,9 +483,15 @@ class AssetModelsController extends Controller
|
||||
$rules[$field] = $validation;
|
||||
}
|
||||
|
||||
$validator = Validator::make($data, $rules);
|
||||
$attributes = [];
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$attributes[$field->db_column] = trim(preg_replace('/_+|snipeit|\d+/', ' ', $field->db_column));
|
||||
}
|
||||
|
||||
$validator = Validator::make($data, $rules)->setAttributeNames($attributes);
|
||||
|
||||
if($validator->fails()){
|
||||
$this->validatorErrors = $validator->errors();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ class AssetModelsFilesController extends Controller
|
||||
$model->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('general.file_upload_success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,11 +119,10 @@ class AssetModelsFilesController extends Controller
|
||||
}
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
// Redirect to the hardware management page
|
||||
|
||||
@@ -45,7 +45,7 @@ class AssetFilesController extends Controller
|
||||
$asset->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
@@ -97,25 +97,19 @@ class AssetFilesController extends Controller
|
||||
*/
|
||||
public function destroy($assetId = null, $fileId = null) : RedirectResponse
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
$this->authorize('update', $asset);
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
if ($asset = Asset::find($assetId)) {
|
||||
$this->authorize('update', $asset);
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
if ($log = Actionlog::find($fileId)) {
|
||||
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'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found'));
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
|
||||
@@ -538,7 +538,7 @@ class AssetsController extends Controller
|
||||
if ($settings->qr_code == '1') {
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
if ($asset) {
|
||||
$size = Helper::barcodeDimensions($settings->barcode_type);
|
||||
$size = Helper::barcodeDimensions($settings->label2_2d_type);
|
||||
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png';
|
||||
|
||||
if (isset($asset->id, $asset->asset_tag)) {
|
||||
@@ -548,7 +548,7 @@ class AssetsController extends Controller
|
||||
return response()->file($qr_file, $header);
|
||||
} else {
|
||||
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
||||
$barcode_obj = $barcode->getBarcodeObj($settings->barcode_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
|
||||
$barcode_obj = $barcode->getBarcodeObj($settings->label2_2d_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', [-2, -2, -2, -2]);
|
||||
file_put_contents($qr_file, $barcode_obj->getPngData());
|
||||
|
||||
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
||||
@@ -573,7 +573,7 @@ class AssetsController extends Controller
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
if ($asset = Asset::withTrashed()->find($assetId)) {
|
||||
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->alt_barcode).'-'.str_slug($asset->asset_tag).'.png';
|
||||
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->label2_1d_type).'-'.str_slug($asset->asset_tag).'.png';
|
||||
|
||||
if (isset($asset->id, $asset->asset_tag)) {
|
||||
if (file_exists($barcode_file)) {
|
||||
@@ -586,7 +586,7 @@ class AssetsController extends Controller
|
||||
|
||||
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
||||
try {
|
||||
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
|
||||
$barcode_obj = $barcode->getBarcodeObj($settings->label2_1d_type, $asset->asset_tag, ($barcode_width < 300 ? $barcode_width : 300), 50);
|
||||
file_put_contents($barcode_file, $barcode_obj->getPngData());
|
||||
|
||||
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
||||
@@ -865,8 +865,8 @@ class AssetsController extends Controller
|
||||
public function quickScan()
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
$dt = Carbon::now()->addMonths(12)->toDateString();
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
return view('hardware/quickscan')->with('next_audit_date', $dt);
|
||||
}
|
||||
|
||||
@@ -883,7 +883,6 @@ class AssetsController extends Controller
|
||||
$this->authorize('audit', Asset::class);
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
$asset = Asset::findOrFail($id);
|
||||
|
||||
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ class ForgotPasswordController extends Controller
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
|
||||
/**
|
||||
* Let's set a max character count here to prevent potential
|
||||
* buffer overflow issues with attackers sending very large
|
||||
* payloads through.
|
||||
* payloads through. The addition of the string rule prevents attackers
|
||||
* sending arrays through and causing 500s
|
||||
*/
|
||||
$request->validate([
|
||||
'username' => ['required', 'max:255'],
|
||||
'username' => ['required', 'max:255', 'string'],
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,22 +103,24 @@ class ResetPasswordController extends Controller
|
||||
], $messages);
|
||||
}
|
||||
|
||||
if ($user->ldap_import != '1') {
|
||||
|
||||
// set the response
|
||||
$response = $broker->reset(
|
||||
$this->credentials($request), function ($user, $password) {
|
||||
$this->resetPassword($user, $password);
|
||||
});
|
||||
// set the response
|
||||
$response = $broker->reset(
|
||||
$this->credentials($request), function ($user, $password) {
|
||||
$this->resetPassword($user, $password);
|
||||
});
|
||||
|
||||
// Check if the password reset above actually worked
|
||||
if ($response == \Password::PASSWORD_RESET) {
|
||||
Log::debug('Password reset for '.$user->username.' worked');
|
||||
return redirect()->guest('login')->with('success', trans('passwords.reset'));
|
||||
// Check if the password reset above actually worked
|
||||
if ($response == \Password::PASSWORD_RESET) {
|
||||
Log::debug('Password reset for ' . $user->username . ' worked');
|
||||
return redirect()->guest('login')->with('success', trans('passwords.reset'));
|
||||
}
|
||||
|
||||
Log::debug('Password reset for ' . $user->username . ' FAILED - this user exists but the token is not valid');
|
||||
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
|
||||
}
|
||||
|
||||
Log::debug('Password reset for '.$user->username.' FAILED - this user exists but the token is not valid');
|
||||
return redirect()->back()->withInput($request->only('email'))->with('success', trans('passwords.reset'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ class ComponentsController extends Controller
|
||||
$this->authorize('delete', $component);
|
||||
|
||||
// Remove the image if one exists
|
||||
if (Storage::disk('public')->exists('components/'.$component->image)) {
|
||||
if ($component->image && Storage::disk('public')->exists('components/' . $component->image)) {
|
||||
try {
|
||||
Storage::disk('public')->delete('components/'.$component->image);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -50,7 +50,7 @@ class ComponentsFilesController extends Controller
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('components.show', $component->id)->with('success', trans('general.file_upload_success'));
|
||||
return redirect()->route('components.show', $component->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
|
||||
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class ComponentsFilesController extends Controller
|
||||
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->back()->withFragment('files')
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class ConsumablesController extends Controller
|
||||
{
|
||||
$this->authorize('create', Consumable::class);
|
||||
|
||||
return view('consumables/edit')->with('category_type', 'consumable')
|
||||
return view('consumables.edit')->with('category_type', 'consumable')
|
||||
->with('item', new Consumable);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class ConsumablesFilesController extends Controller
|
||||
}
|
||||
|
||||
|
||||
return redirect()->route('consumables.show', $consumable->id)->with('success', trans('general.file_upload_success'));
|
||||
return redirect()->route('consumables.show', $consumable->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
|
||||
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class ConsumablesFilesController extends Controller
|
||||
|
||||
$log->delete();
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->back()->withFragment('files')
|
||||
->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class CustomFieldsController extends Controller
|
||||
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
|
||||
"show_in_listview" => $request->get("show_in_listview", 0),
|
||||
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
|
||||
"user_id" => auth()->id()
|
||||
"created_by" => auth()->id()
|
||||
]);
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class LicenseCheckinController extends Controller
|
||||
|
||||
if (! $license->reassignable) {
|
||||
// Not allowed to checkin
|
||||
Session::flash('error', 'License not reassignable.');
|
||||
Session::flash('error', trans('admin/licenses/message.checkin.not_reassignable') . '.');
|
||||
|
||||
return redirect()->back()->withInput();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ class ModalController extends Controller
|
||||
'statuslabel',
|
||||
'supplier',
|
||||
'upload-file',
|
||||
'user',
|
||||
'user',
|
||||
'add-note',
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -99,9 +99,13 @@ class ProfileController extends Controller
|
||||
* User change email page.
|
||||
*
|
||||
*/
|
||||
public function password() : View
|
||||
public function password() : View | RedirectResponse
|
||||
{
|
||||
|
||||
$user = auth()->user();
|
||||
if ($user->ldap_import=='1') {
|
||||
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
|
||||
}
|
||||
return view('account/change-password', compact('user'));
|
||||
}
|
||||
|
||||
@@ -116,7 +120,7 @@ class ProfileController extends Controller
|
||||
|
||||
$user = auth()->user();
|
||||
if ($user->ldap_import == '1') {
|
||||
return redirect()->route('account.password.index')->with('error', trans('admin/users/message.error.password_ldap'));
|
||||
return redirect()->route('account')->with('error', trans('admin/users/message.error.password_ldap'));
|
||||
}
|
||||
|
||||
$rules = [
|
||||
|
||||
80
app/Http/Controllers/ReportTemplatesController.php
Normal file
80
app/Http/Controllers/ReportTemplatesController.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use App\Models\ReportTemplate;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ReportTemplatesController extends Controller
|
||||
{
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
// Ignore "options" rules since data does not come in under that key...
|
||||
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
|
||||
|
||||
$report = $request->user()->reportTemplates()->create([
|
||||
'name' => $validated['name'],
|
||||
'options' => $request->except(['_token', 'name']),
|
||||
]);
|
||||
|
||||
session()->flash('success', trans('admin/reports/message.create.success'));
|
||||
|
||||
return redirect()->route('report-templates.show', $report->id);
|
||||
}
|
||||
|
||||
public function show(ReportTemplate $reportTemplate)
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$customfields = CustomField::get();
|
||||
$report_templates = ReportTemplate::orderBy('name')->get();
|
||||
|
||||
return view('reports/custom', [
|
||||
'customfields' => $customfields,
|
||||
'report_templates' => $report_templates,
|
||||
'template' => $reportTemplate,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(ReportTemplate $reportTemplate)
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
return view('reports/custom', [
|
||||
'customfields' => CustomField::get(),
|
||||
'template' => $reportTemplate,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, ReportTemplate $reportTemplate): RedirectResponse
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
// Ignore "options" rules since data does not come in under that key...
|
||||
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
|
||||
|
||||
$reportTemplate->update([
|
||||
'name' => $validated['name'],
|
||||
'options' => $request->except(['_token', 'name']),
|
||||
]);
|
||||
|
||||
session()->flash('success', trans('admin/reports/message.update.success'));
|
||||
|
||||
return redirect()->route('report-templates.show', $reportTemplate->id);
|
||||
}
|
||||
|
||||
public function destroy(ReportTemplate $reportTemplate): RedirectResponse
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$reportTemplate->delete();
|
||||
|
||||
return redirect()->route('reports/custom')
|
||||
->with('success', trans('admin/reports/message.delete.success'));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Mail\CheckoutAssetMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
@@ -10,14 +11,18 @@ use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\AssetMaintenance;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Company;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Depreciation;
|
||||
use App\Models\License;
|
||||
use App\Models\ReportTemplate;
|
||||
use App\Models\Setting;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use League\Csv\Reader;
|
||||
@@ -259,7 +264,7 @@ class ReportsController extends Controller
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
Log::debug('Added headers: '.$executionTime);
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target', 'location')
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target', 'location', 'adminuser')
|
||||
->orderBy('created_at', 'DESC')
|
||||
->chunk(20, function ($actionlogs) use ($handle) {
|
||||
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
|
||||
@@ -286,7 +291,7 @@ class ReportsController extends Controller
|
||||
|
||||
$row = [
|
||||
$actionlog->created_at,
|
||||
($actionlog->admin) ? e($actionlog->admin->getFullNameAttribute()) : '',
|
||||
($actionlog->adminuser) ? e($actionlog->adminuser->getFullNameAttribute()) : '',
|
||||
$actionlog->present()->actionType(),
|
||||
e($actionlog->itemType()),
|
||||
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
|
||||
@@ -392,12 +397,27 @@ class ReportsController extends Controller
|
||||
* @see ReportsController::postCustomReport() method that generates the CSV
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function getCustomReport() : View
|
||||
public function getCustomReport(Request $request) : View
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$customfields = CustomField::get();
|
||||
$report_templates = ReportTemplate::orderBy('name')->get();
|
||||
|
||||
return view('reports/custom')->with('customfields', $customfields);
|
||||
// The view needs a template to render correctly, even if it is empty...
|
||||
$template = new ReportTemplate;
|
||||
|
||||
// Set the report's input values in the cases we were redirected back
|
||||
// with validation errors so the report is populated as expected.
|
||||
if ($request->old()) {
|
||||
$template->name = $request->old('name');
|
||||
$template->options = $request->old();
|
||||
}
|
||||
|
||||
return view('reports/custom', [
|
||||
'customfields' => $customfields,
|
||||
'report_templates' => $report_templates,
|
||||
'template' => $template,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1091,28 +1111,31 @@ class ReportsController extends Controller
|
||||
$this->authorize('reports.view');
|
||||
$showDeleted = $deleted == 'deleted';
|
||||
|
||||
/**
|
||||
* Get all assets with pending checkout acceptances
|
||||
*/
|
||||
if($showDeleted) {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->withTrashed()->with(['assignedTo' , 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
} else {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->with(['assignedTo' => function ($query) {
|
||||
$query->withTrashed();
|
||||
}, 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
$query = CheckoutAcceptance::pending()
|
||||
->where('checkoutable_type', 'App\Models\Asset')
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $query) {
|
||||
$query->morphWith([
|
||||
AssetModel::class => ['model'],
|
||||
Company::class => ['company'],
|
||||
Asset::class => ['assignedTo'],
|
||||
])->with('model.category');
|
||||
},
|
||||
'assignedTo' => function($query){
|
||||
$query->withTrashed();
|
||||
}
|
||||
]);
|
||||
|
||||
if ($showDeleted) {
|
||||
$query->withTrashed();
|
||||
}
|
||||
|
||||
$assetsForReport = $acceptances
|
||||
->filter(function ($acceptance) {
|
||||
$acceptance_checkoutable_flag = false;
|
||||
if ($acceptance->checkoutable){
|
||||
$acceptance_checkoutable_flag = $acceptance->checkoutable->checkedOutToUser();
|
||||
}
|
||||
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset' && $acceptance_checkoutable_flag;
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
$assetsForReport = $query->get()
|
||||
->map(function ($acceptance) {
|
||||
return [
|
||||
'assetItem' => $acceptance->checkoutable,
|
||||
'acceptance' => $acceptance,
|
||||
];
|
||||
});
|
||||
|
||||
return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
|
||||
@@ -1150,24 +1173,17 @@ class ReportsController extends Controller
|
||||
}
|
||||
$logItem = $logItem_res[0];
|
||||
}
|
||||
|
||||
$email = $assetItem->assignedTo?->email;
|
||||
$locale = $assetItem->assignedTo?->locale;
|
||||
// Only send notification if assigned
|
||||
if ($assetItem->assignedTo) {
|
||||
if ($locale && $email) {
|
||||
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note))->locale($locale));
|
||||
|
||||
if (!$assetItem->assignedTo->locale) {
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$assetItem->assignedTo,
|
||||
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
||||
);
|
||||
} else {
|
||||
Notification::send(
|
||||
$assetItem->assignedTo,
|
||||
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
||||
);
|
||||
} elseif ($email) {
|
||||
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)));
|
||||
}
|
||||
}
|
||||
|
||||
if ($assetItem->assignedTo->email == ''){
|
||||
if ($email == ''){
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ class SettingsController extends Controller
|
||||
$settings->next_auto_tag_base = 1;
|
||||
$settings->auto_increment_assets = $request->input('auto_increment_assets', 0);
|
||||
$settings->auto_increment_prefix = $request->input('auto_increment_prefix');
|
||||
$settings->zerofill_count = $request->input('zerofill_count') ?: 0;
|
||||
|
||||
if ((! $user->isValid()) || (! $settings->isValid())) {
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors())->withErrors($settings->getErrors());
|
||||
@@ -334,6 +335,8 @@ class SettingsController extends Controller
|
||||
$setting->depreciation_method = $request->input('depreciation_method');
|
||||
$setting->dash_chart_type = $request->input('dash_chart_type');
|
||||
$setting->profile_edit = $request->input('profile_edit', 0);
|
||||
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
|
||||
|
||||
|
||||
if ($request->input('per_page') != '') {
|
||||
$setting->per_page = $request->input('per_page');
|
||||
@@ -693,48 +696,6 @@ class SettingsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($setting->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form to allow a super admin to update settings.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function getBarcodes() : View
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
$is_gd_installed = extension_loaded('gd');
|
||||
|
||||
return view('settings.barcodes', compact('setting'))->with('is_gd_installed', $is_gd_installed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves settings from form.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postBarcodes(Request $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->qr_code = $request->input('qr_code', '0');
|
||||
$setting->alt_barcode = $request->input('alt_barcode');
|
||||
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
|
||||
$setting->barcode_type = $request->input('barcode_type');
|
||||
$setting->qr_text = $request->input('qr_text');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
->with('success', trans('admin/settings/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($setting->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form to allow a super admin to update settings.
|
||||
*
|
||||
@@ -760,8 +721,11 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function getLabels() : View
|
||||
{
|
||||
$is_gd_installed = extension_loaded('gd');
|
||||
|
||||
return view('settings.labels')
|
||||
->with('setting', Setting::getSettings())
|
||||
->with('is_gd_installed', $is_gd_installed)
|
||||
->with('customFields', CustomField::where('field_encrypted', '=', 0)->get());
|
||||
}
|
||||
|
||||
@@ -797,9 +761,13 @@ class SettingsController extends Controller
|
||||
$setting->labels_pagewidth = $request->input('labels_pagewidth');
|
||||
$setting->labels_pageheight = $request->input('labels_pageheight');
|
||||
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
|
||||
$setting->labels_display_company_name = $request->input('labels_display_company_name', '0');
|
||||
|
||||
|
||||
//Barcodes
|
||||
$setting->qr_code = $request->input('qr_code', '0');
|
||||
//1D-Barcode
|
||||
$setting->alt_barcode_enabled = $request->input('alt_barcode_enabled', '0');
|
||||
//QR-Code
|
||||
$setting->qr_text = $request->input('qr_text');
|
||||
|
||||
if ($request->filled('labels_display_name')) {
|
||||
$setting->labels_display_name = 1;
|
||||
|
||||
@@ -70,7 +70,7 @@ class BulkUsersController extends Controller
|
||||
// bulk password reset, just do the thing
|
||||
} elseif ($request->input('bulk_actions') == 'bulkpasswordreset') {
|
||||
foreach ($users as $user) {
|
||||
if (($user->activated == '1') && ($user->email != '')) {
|
||||
if (($user->activated == '1') && ($user->email != '') && ($user->ldap_import != '1')) {
|
||||
$credentials = ['email' => $user->email];
|
||||
Password::sendResetLink($credentials/* , function (Message $message) {
|
||||
$message->subject($this->getEmailSubject()); // TODO - I'm not sure if we still need this, but this second parameter is no longer accepted in later Laravel versions.
|
||||
@@ -323,7 +323,7 @@ class BulkUsersController extends Controller
|
||||
$logAction->item_type = $itemType;
|
||||
$logAction->target_id = $item->assigned_to;
|
||||
$logAction->target_type = User::class;
|
||||
$logAction->created_at = auth()->id();
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->note = 'Bulk checkin items';
|
||||
$logAction->logaction('checkin from');
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class UserFilesController extends Controller
|
||||
$logActions[] = $logAction;
|
||||
}
|
||||
// dd($logActions);
|
||||
return redirect()->back()->with('success', trans('admin/users/message.upload.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
|
||||
|
||||
@@ -87,7 +87,7 @@ class UserFilesController extends Controller
|
||||
|
||||
if (Storage::exists($rel_path.'/'.$filename)) {
|
||||
Storage::delete($rel_path.'/'.$filename);
|
||||
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
|
||||
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.deletefile.success'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class UsersController extends Controller
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
$groups = Group::pluck('name', 'id');
|
||||
$companies = Company::pluck('name', 'id');
|
||||
|
||||
$userGroups = collect();
|
||||
|
||||
@@ -75,7 +76,7 @@ class UsersController extends Controller
|
||||
|
||||
$user = new User;
|
||||
|
||||
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
|
||||
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions', 'companies'))
|
||||
->with('user', $user);
|
||||
}
|
||||
|
||||
@@ -142,6 +143,8 @@ class UsersController extends Controller
|
||||
$user->groups()->sync([]);
|
||||
}
|
||||
|
||||
$user->companies()->sync($request->input('companies'));
|
||||
|
||||
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
|
||||
// Send the credentials through email
|
||||
$data = [];
|
||||
@@ -192,13 +195,14 @@ class UsersController extends Controller
|
||||
|
||||
$permissions = config('permissions');
|
||||
$groups = Group::pluck('name', 'id');
|
||||
$companies = Company::pluck('name', 'id');
|
||||
|
||||
$userGroups = $user->groups()->pluck('name', 'id');
|
||||
$user->permissions = $user->decodePermissions();
|
||||
$userPermissions = Helper::selectedPermissionsArray($permissions, $user->permissions);
|
||||
$permissions = $this->filterDisplayable($permissions);
|
||||
|
||||
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))->with('item', $user);
|
||||
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions', 'companies'))->with('item', $user);
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
@@ -302,6 +306,7 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
$user->permissions = json_encode($permissions_array);
|
||||
$user->companies()->sync($request->input('companies'));
|
||||
|
||||
// Handle uploaded avatar
|
||||
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
|
||||
|
||||
@@ -21,9 +21,14 @@ class AssetCheckinRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
];
|
||||
$rules = [];
|
||||
|
||||
if($settings->require_checkinout_notes) {
|
||||
$rules['note'] = 'string|required';
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
|
||||
@@ -21,10 +21,12 @@ class AssetCheckoutRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
$rules = [
|
||||
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
|
||||
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
|
||||
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
|
||||
'assigned_user' => 'numeric|nullable|required_without_all:assigned_asset,assigned_location',
|
||||
'assigned_asset' => 'numeric|nullable|required_without_all:assigned_user,assigned_location',
|
||||
'assigned_location' => 'numeric|nullable|required_without_all:assigned_user,assigned_asset',
|
||||
'status_id' => 'exists:status_labels,id,deployable,1',
|
||||
'checkout_to_type' => 'required|in:asset,location,user',
|
||||
'checkout_at' => [
|
||||
@@ -35,7 +37,11 @@ class AssetCheckoutRequest extends Request
|
||||
'nullable',
|
||||
'date'
|
||||
],
|
||||
];
|
||||
];
|
||||
|
||||
if($settings->require_checkinout_notes) {
|
||||
$rules['note'] = 'required|string';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
@@ -46,8 +46,6 @@ class UploadFileRequest extends Request
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
|
||||
|
||||
|
||||
Log::debug("Your filetype IS: ".$file->getMimeType());
|
||||
// Check for SVG and sanitize it
|
||||
if ($file->getMimeType() === 'image/svg+xml') {
|
||||
Log::debug('This is an SVG');
|
||||
@@ -66,7 +64,6 @@ class UploadFileRequest extends Request
|
||||
|
||||
} else {
|
||||
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
|
||||
Log::debug("Here are the '$put_results' (should be 0 or 1 or true or false or something?)");
|
||||
}
|
||||
return $file_name;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class AccessoriesTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
|
||||
public function transformCheckedoutAccessory($accessory_checkouts, $total)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
@@ -77,9 +77,13 @@ class AccessoriesTransformer
|
||||
$array[] = [
|
||||
'id' => $checkout->id,
|
||||
'assigned_to' => $this->transformAssignedTo($checkout),
|
||||
'checkout_notes' => e($checkout->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||
'available_actions' => ['checkin' => true],
|
||||
'note' => $checkout->note ? e($checkout->note) : null,
|
||||
'created_by' => $checkout->adminuser ? [
|
||||
'id' => (int) $checkout->adminuser->id,
|
||||
'name'=> e($checkout->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||
'available_actions' => Gate::allows('checkout', Accessory::class) ? ['checkin' => true] : ['checkin' => false],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -89,22 +93,11 @@ class AccessoriesTransformer
|
||||
public function transformAssignedTo($accessoryCheckout)
|
||||
{
|
||||
if ($accessoryCheckout->checkedOutToUser()) {
|
||||
return [
|
||||
'id' => (int) $accessoryCheckout->assigned->id,
|
||||
'username' => e($accessoryCheckout->assigned->username),
|
||||
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
|
||||
'first_name'=> e($accessoryCheckout->assigned->first_name),
|
||||
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
|
||||
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
|
||||
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
|
||||
'type' => 'user',
|
||||
];
|
||||
return (new UsersTransformer)->transformUserCompact($accessoryCheckout->assigned);
|
||||
} elseif ($accessoryCheckout->checkedOutToLocation()) {
|
||||
return (new LocationsTransformer())->transformLocationCompact($accessoryCheckout->assigned);
|
||||
} elseif ($accessoryCheckout->checkedOutToAsset()) {
|
||||
return (new AssetsTransformer())->transformAssetCompact($accessoryCheckout->assigned);
|
||||
}
|
||||
|
||||
return $accessoryCheckout->assigned ? [
|
||||
'id' => $accessoryCheckout->assigned->id,
|
||||
'name' => e($accessoryCheckout->assigned->display_name),
|
||||
'type' => $accessoryCheckout->assignedType(),
|
||||
] : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,15 @@ class AssetMaintenancesTransformer
|
||||
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
|
||||
'asset_tag'=> e($assetmaintenance->asset->asset_tag),
|
||||
'serial'=> e($assetmaintenance->asset->serial),
|
||||
'deleted_at'=> e($assetmaintenance->asset->deleted_at),
|
||||
'created_at'=> e($assetmaintenance->asset->created_at),
|
||||
'deleted_at'=> Helper::getFormattedDateObject($assetmaintenance->asset->deleted_at, 'datetime'),
|
||||
'created_at' => Helper::getFormattedDateObject($assetmaintenance->asset->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->asset->updated_at, 'datetime'),
|
||||
] : null,
|
||||
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->model->id,
|
||||
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
|
||||
] : null,
|
||||
'status_label' => ($assetmaintenance->asset->assetstatus) ? [
|
||||
'status_label' => (($assetmaintenance->asset) && ($assetmaintenance->asset->assetstatus)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->assetstatus->id,
|
||||
'name'=> e($assetmaintenance->asset->assetstatus->name),
|
||||
'status_type'=> e($assetmaintenance->asset->assetstatus->getStatuslabelType()),
|
||||
@@ -79,7 +80,7 @@ class AssetMaintenancesTransformer
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => (Gate::allows('update', Asset::class) && ($assetmaintenance->asset->deleted_at=='')) ? true : false,
|
||||
'update' => (Gate::allows('update', Asset::class) && ((($assetmaintenance->asset) && $assetmaintenance->asset->deleted_at==''))) ? true : false,
|
||||
'delete' => Gate::allows('delete', Asset::class),
|
||||
];
|
||||
|
||||
|
||||
@@ -87,6 +87,41 @@ class AssetModelsTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformAssetModelFiles($assetmodel, $total)
|
||||
{
|
||||
|
||||
$array = [];
|
||||
foreach ($assetmodel->uploads as $file) {
|
||||
$array[] = self::transformAssetModelFile($file, $assetmodel);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformAssetModelFile($file, $assetmodel)
|
||||
{
|
||||
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'filename' => e($file->filename),
|
||||
'url' => route('show/modelfile', [$assetmodel->id, $file->id]),
|
||||
'created_by' => ($file->adminuser) ? [
|
||||
'id' => (int) $file->adminuser->id,
|
||||
'name'=> e($file->adminuser->present()->fullName),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'delete' => (Gate::allows('update', AssetModel::class) && ($assetmodel->deleted_at == '')),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformAssetModelsDatatable($assetmodels)
|
||||
{
|
||||
return (new DatatablesTransformer)->transformDatatables($assetmodels);
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AssetsTransformer
|
||||
{
|
||||
@@ -225,7 +227,7 @@ class AssetsTransformer
|
||||
public function transformRequestedAsset(Asset $asset)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $asset->id,
|
||||
'id' => (int)$asset->id,
|
||||
'name' => e($asset->name),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
@@ -234,7 +236,7 @@ class AssetsTransformer
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
|
||||
'location' => ($asset->location) ? e($asset->location->name) : null,
|
||||
'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
|
||||
'status' => ($asset->assetstatus) ? $asset->present()->statusMeta : null,
|
||||
'assigned_to_self' => ($asset->assigned_to == auth()->id()),
|
||||
];
|
||||
|
||||
@@ -244,7 +246,7 @@ class AssetsTransformer
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
// Only display this if it's allowed via the custom field setting
|
||||
if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) {
|
||||
if (($field->field_encrypted == '0') && ($field->show_in_requestable_list == '1')) {
|
||||
|
||||
$value = $asset->{$field->db_column};
|
||||
if (($field->format == 'DATE') && (!is_null($value)) && ($value != '')) {
|
||||
@@ -268,7 +270,61 @@ class AssetsTransformer
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function transformAssetCompact(Asset $asset)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $asset->id,
|
||||
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
|
||||
'type' => 'asset',
|
||||
'name' => e($asset->present()->fullName()),
|
||||
'model' => ($asset->model) ? e($asset->model->name) : null,
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutAccessories($accessory_checkouts, $total)
|
||||
{
|
||||
|
||||
$array = [];
|
||||
foreach ($accessory_checkouts as $checkout) {
|
||||
$array[] = self::transformCheckedoutAccessory($checkout);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
|
||||
{
|
||||
|
||||
$array = [
|
||||
'id' => $accessory_checkout->id,
|
||||
'accessory' => [
|
||||
'id' => $accessory_checkout->accessory->id,
|
||||
'name' => $accessory_checkout->accessory->name,
|
||||
],
|
||||
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
|
||||
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
|
||||
'created_by' => $accessory_checkout->adminuser ? [
|
||||
'id' => (int) $accessory_checkout->adminuser->id,
|
||||
'name'=> e($accessory_checkout->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => false,
|
||||
'checkin' => Gate::allows('checkin', Accessory::class),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class DepreciationReportTransformer
|
||||
/**
|
||||
* Override the previously set null values if there is a valid model and associated depreciation
|
||||
*/
|
||||
if (($asset->model) && ($asset->model->depreciation)) {
|
||||
if (($asset->model) && ($asset->model->depreciation) && ($asset->model->depreciation->months !== 0)) {
|
||||
$depreciated_value = Helper::formatCurrencyOutput($asset->getDepreciatedValue());
|
||||
$monthly_depreciation =Helper::formatCurrencyOutput($asset->purchase_cost / $asset->model->depreciation->months);
|
||||
$diff = Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
@@ -45,6 +47,8 @@ class LocationsTransformer
|
||||
'zip' => ($location->zip) ? e($location->zip) : null,
|
||||
'phone' => ($location->phone!='') ? e($location->phone): null,
|
||||
'fax' => ($location->fax!='') ? e($location->fax): null,
|
||||
'accessories_count' => (int) $location->accessories_count,
|
||||
'assigned_accessories_count' => (int) $location->assigned_accessories_count,
|
||||
'assigned_assets_count' => (int) $location->assigned_assets_count,
|
||||
'assets_count' => (int) $location->assets_count,
|
||||
'rtd_assets_count' => (int) $location->rtd_assets_count,
|
||||
@@ -76,4 +80,75 @@ class LocationsTransformer
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function transformCheckedoutAccessories($accessory_checkouts, $total)
|
||||
{
|
||||
|
||||
$array = [];
|
||||
foreach ($accessory_checkouts as $checkout) {
|
||||
$array[] = self::transformCheckedoutAccessory($checkout);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
|
||||
{
|
||||
|
||||
$array = [
|
||||
'id' => $accessory_checkout->id,
|
||||
'accessory' => [
|
||||
'id' => $accessory_checkout->accessory->id,
|
||||
'name' => $accessory_checkout->accessory->name,
|
||||
],
|
||||
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
|
||||
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
|
||||
'created_by' => $accessory_checkout->adminuser ? [
|
||||
'id' => (int) $accessory_checkout->adminuser->id,
|
||||
'name'=> e($accessory_checkout->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'checkout' => false,
|
||||
'checkin' => Gate::allows('checkin', Accessory::class),
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This gives a compact view of the location data without any additional relational queries,
|
||||
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
|
||||
* have not been easy/lazy loaded already
|
||||
*
|
||||
* @param Location $location
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function transformLocationCompact(Location $location = null)
|
||||
{
|
||||
if ($location) {
|
||||
|
||||
$array = [
|
||||
'id' => (int) $location->id,
|
||||
'image' => ($location->image) ? Storage::disk('public')->url('locations/'.e($location->image)) : null,
|
||||
'type' => "location",
|
||||
'name' => e($location->name),
|
||||
'created_by' => $location->adminuser ? [
|
||||
'id' => (int) $location->adminuser->id,
|
||||
'name'=> e($location->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Asset;
|
||||
use App\Models\PredefinedKit;
|
||||
use App\Models\SnipeModel;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
@@ -42,7 +43,7 @@ class PredefinedKitsTransformer
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', PredefinedKit::class),
|
||||
'delete' => Gate::allows('delete', PredefinedKit::class),
|
||||
'checkout' => Gate::allows('checkout', PredefinedKit::class),
|
||||
'checkout' => Gate::allows('checkout', Asset::class),
|
||||
// 'clone' => Gate::allows('create', PredefinedKit::class),
|
||||
// 'restore' => Gate::allows('create', PredefinedKit::class),
|
||||
];
|
||||
|
||||
@@ -4,8 +4,8 @@ namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class UsersTransformer
|
||||
{
|
||||
@@ -55,7 +55,6 @@ class UsersTransformer
|
||||
'name'=> e($user->userloc->name),
|
||||
] : null,
|
||||
'notes'=> Helper::parseEscapedMarkedownInline($user->notes),
|
||||
'permissions' => $user->decodePermissions(),
|
||||
'activated' => ($user->activated == '1') ? true : false,
|
||||
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
|
||||
'ldap_import' => ($user->ldap_import == '1') ? true : false,
|
||||
@@ -67,7 +66,9 @@ class UsersTransformer
|
||||
'consumables_count' => (int) $user->consumables_count,
|
||||
'manages_users_count' => (int) $user->manages_users_count,
|
||||
'manages_locations_count' => (int) $user->manages_locations_count,
|
||||
'company' => ($user->company) ? ['id' => (int) $user->company->id, 'name'=> e($user->company->name)] : null,
|
||||
'company' => ($user->company) ? ['id' => (int) $user->company->id, 'name'=> e($user->company->name)] : null, // Legacy
|
||||
'companies' => ($user->companies) ? [$user->companies->pluck('name', 'id')] : [],
|
||||
'permissions' => $user->decodePermissions(),
|
||||
'created_by' => ($user->createdBy) ? [
|
||||
'id' => (int) $user->createdBy->id,
|
||||
'name'=> e($user->createdBy->present()->fullName),
|
||||
@@ -106,6 +107,37 @@ class UsersTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gives a compact view of the user data without any additional relational queries,
|
||||
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
|
||||
* have not been easy/lazy loaded already
|
||||
*
|
||||
* @param User $user
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function transformUserCompact(User $user) : array
|
||||
{
|
||||
|
||||
$array = [
|
||||
'id' => (int) $user->id,
|
||||
'image' => e($user->present()->gravatar) ?? null,
|
||||
'type' => 'user',
|
||||
'name' => e($user->getFullNameAttribute()),
|
||||
'first_name' => e($user->first_name),
|
||||
'last_name' => e($user->last_name),
|
||||
'username' => e($user->username),
|
||||
'created_by' => $user->adminuser ? [
|
||||
'id' => (int) $user->adminuser->id,
|
||||
'name'=> e($user->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
|
||||
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformUsersDatatable($users)
|
||||
{
|
||||
return (new DatatablesTransformer)->transformDatatables($users);
|
||||
|
||||
@@ -39,6 +39,7 @@ abstract class Importer
|
||||
* @var array
|
||||
*/
|
||||
private $defaultFieldMap = [
|
||||
'id' => 'id',
|
||||
'asset_tag' => 'asset tag',
|
||||
'activated' => 'activated',
|
||||
'category' => 'category',
|
||||
|
||||
@@ -456,14 +456,13 @@ class ItemImporter extends Importer
|
||||
{
|
||||
if (empty($asset_location)) {
|
||||
$this->log('No location given, so none created.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$location = Location::where(['name' => $asset_location])->first();
|
||||
|
||||
if ($location) {
|
||||
$this->log('Location '.$asset_location.' already exists');
|
||||
|
||||
return $location->id;
|
||||
}
|
||||
// No matching locations in the collection, create a new one.
|
||||
|
||||
@@ -38,8 +38,16 @@ class LocationImporter extends ItemImporter
|
||||
{
|
||||
|
||||
$editingLocation = false;
|
||||
|
||||
$location = Location::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
|
||||
|
||||
if ($this->findCsvMatch($row, 'id')!='') {
|
||||
// Override location if an ID was given
|
||||
\Log::debug('Finding location by ID: '.$this->findCsvMatch($row, 'id'));
|
||||
$location = Location::find($this->findCsvMatch($row, 'id'));
|
||||
}
|
||||
|
||||
|
||||
if ($location) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Location '.$this->item['name'].' already exists');
|
||||
@@ -95,6 +103,7 @@ class LocationImporter extends ItemImporter
|
||||
|
||||
} else {
|
||||
Log::debug($location->getErrors());
|
||||
$this->logError($location, 'Location "'.$this->item['name'].'"');
|
||||
return $location->errors;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class CheckoutableListener
|
||||
*/
|
||||
public function onCheckedOut($event)
|
||||
{
|
||||
if ($this->shouldNotSendAnyNotifications($event->checkoutable)){
|
||||
if ($this->shouldNotSendAnyNotifications($event->checkoutable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class CheckoutableListener
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
$adminCcEmailsArray = [];
|
||||
|
||||
if($settings->admin_cc_email !== '') {
|
||||
if ($settings->admin_cc_email !== '') {
|
||||
$adminCcEmail = $settings->admin_cc_email;
|
||||
$adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail));
|
||||
}
|
||||
@@ -65,7 +65,7 @@ class CheckoutableListener
|
||||
$mailable = $this->getCheckoutMailType($event, $acceptance);
|
||||
$notifiable = $this->getNotifiables($event);
|
||||
|
||||
if (!$event->checkedOutTo->locale){
|
||||
if ($event->checkedOutTo->locale) {
|
||||
$mailable->locale($event->checkedOutTo->locale);
|
||||
}
|
||||
// Send email notifications
|
||||
@@ -77,41 +77,50 @@ class CheckoutableListener
|
||||
* 3. The item should send an email at check-in/check-out
|
||||
*/
|
||||
|
||||
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
|
||||
$this->checkoutableShouldSendEmail($event)) {
|
||||
Log::info('Sending checkout email, Locale: ' . ($event->checkedOutTo->locale ?? 'default'));
|
||||
if (!empty($notifiable)) {
|
||||
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
|
||||
} elseif (!empty($ccEmails)) {
|
||||
Mail::cc($ccEmails)->send($mailable);
|
||||
}
|
||||
Log::info('Checkout Mail sent.');
|
||||
if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() ||
|
||||
$this->checkoutableShouldSendEmail($event)) {
|
||||
Log::info('Sending checkout email, Locale: ' . ($event->checkedOutTo->locale ?? 'default'));
|
||||
if (!empty($notifiable)) {
|
||||
Mail::to($notifiable)->cc($ccEmails)->send($mailable);
|
||||
} elseif (!empty($ccEmails)) {
|
||||
Mail::cc($ccEmails)->send($mailable);
|
||||
}
|
||||
Log::info('Checkout Mail sent.');
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during checkout email: " . $e->getMessage());
|
||||
}
|
||||
// Send Webhook notification
|
||||
try{
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
if ($this->newMicrosoftTeamsWebhookEnabled()) {
|
||||
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
|
||||
} else {
|
||||
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckoutNotification($event, $acceptance));
|
||||
}
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
if ($this->newMicrosoftTeamsWebhookEnabled()) {
|
||||
$message = $this->getCheckoutNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
|
||||
} else {
|
||||
|
||||
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckoutNotification($event, $acceptance));
|
||||
}
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail') );
|
||||
} catch (Exception $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Notify the user and post to webhook about the checked in checkoutable
|
||||
*/
|
||||
@@ -147,7 +156,7 @@ class CheckoutableListener
|
||||
$ccEmails = array_filter($adminCcEmailsArray);
|
||||
$mailable = $this->getCheckinMailType($event);
|
||||
$notifiable = $this->getNotifiables($event);
|
||||
if (!$event->checkedOutTo->locale){
|
||||
if ($event->checkedOutTo->locale){
|
||||
$mailable->locale($event->checkedOutTo->locale);
|
||||
}
|
||||
// Send email notifications
|
||||
@@ -178,18 +187,24 @@ class CheckoutableListener
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
if ($this->newMicrosoftTeamsWebhookEnabled()) {
|
||||
$message = $this->getCheckinNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
|
||||
} else {
|
||||
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
$message = $this->getCheckinNotification($event)->toMicrosoftTeams();
|
||||
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
|
||||
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
|
||||
} else {
|
||||
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
|
||||
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail'));
|
||||
} catch (Exception $e) {
|
||||
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
|
||||
Log::error(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
|
||||
'error' => $e->getMessage(),
|
||||
'webhook_endpoint' => Setting::getSettings()->webhook_endpoint,
|
||||
'event' => $event,
|
||||
]);
|
||||
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) .trans('admin/settings/message.webhook.webhook_fail'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +297,7 @@ class CheckoutableListener
|
||||
];
|
||||
$mailable= $lookup[get_class($event->checkoutable)];
|
||||
|
||||
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $event->note, $acceptance);
|
||||
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note);
|
||||
|
||||
}
|
||||
private function getCheckinMailType($event){
|
||||
@@ -307,9 +322,16 @@ class CheckoutableListener
|
||||
return $event->checkedOutTo->manager?->email ?? '';
|
||||
}
|
||||
else{
|
||||
return $event->checkedOutTo->email;
|
||||
return $event->checkedOutTo?->email ?? '';
|
||||
}
|
||||
}
|
||||
private function webhookSelected(){
|
||||
if(Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general'){
|
||||
return 'slack';
|
||||
}
|
||||
|
||||
return Setting::getSettings()->webhook_selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Events\ItemAccepted;
|
||||
use App\Events\ItemDeclined;
|
||||
use App\Events\LicenseCheckedIn;
|
||||
use App\Events\LicenseCheckedOut;
|
||||
use App\Events\NoteAdded;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\User;
|
||||
use App\Models\LicenseSeat;
|
||||
@@ -128,6 +129,23 @@ class LogListener
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note is added to action log
|
||||
*
|
||||
*/
|
||||
public function onNoteAdded(NoteAdded $event)
|
||||
{
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $event->itemNoteAddedOn->id;
|
||||
$logaction->item_type = get_class($event->itemNoteAddedOn);
|
||||
$logaction->note = $event->note; //this is the received alphanumeric text from the box
|
||||
$logaction->created_by = $event->noteAddedBy->id;
|
||||
$logaction->action_type = 'note_added';
|
||||
$logaction->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
@@ -141,6 +159,7 @@ class LogListener
|
||||
'CheckoutAccepted',
|
||||
'CheckoutDeclined',
|
||||
'UserMerged',
|
||||
'NoteAdded',
|
||||
];
|
||||
|
||||
foreach ($list as $event) {
|
||||
|
||||
@@ -24,7 +24,15 @@ class CustomFieldSetDefaultValuesForModel extends Component
|
||||
$this->fieldset_id = $this->model?->fieldset_id;
|
||||
$this->add_default_values = ($this->model?->defaultValues->count() > 0);
|
||||
|
||||
|
||||
$this->initializeSelectedValuesArray();
|
||||
if (session()->has('errors')) {
|
||||
$errors = session('errors')->keys();
|
||||
$selectedValuesKeys = array_keys($this->selectedValues);
|
||||
if (count(array_intersect($selectedValuesKeys, $errors)) > 0) {
|
||||
$this->add_default_values = true;
|
||||
};
|
||||
}
|
||||
$this->populatedSelectedValuesArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ class Importer extends Component
|
||||
];
|
||||
|
||||
$this->locations_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'name' => trans('general.item_name_var', ['item' => trans('general.location')]),
|
||||
'address' => trans('general.address'),
|
||||
'address2' => trans('general.importer.address2'),
|
||||
@@ -400,7 +401,6 @@ class Importer extends Component
|
||||
'requestable',
|
||||
'Requestable',
|
||||
],
|
||||
|
||||
'gravatar' =>
|
||||
[
|
||||
'gravatar',
|
||||
|
||||
@@ -159,7 +159,7 @@ class SlackSettingsForm extends Component
|
||||
]);
|
||||
|
||||
try {
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload]);
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, ['headers' => ['Content-Type' => 'application/json']]]);
|
||||
|
||||
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
|
||||
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
|
||||
@@ -224,7 +224,7 @@ class SlackSettingsForm extends Component
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders([
|
||||
'content-type' => 'applications/json',
|
||||
'content-type' => 'application/json',
|
||||
])->post($this->webhook_endpoint,
|
||||
$payload)->throw();
|
||||
|
||||
@@ -259,7 +259,7 @@ class SlackSettingsForm extends Component
|
||||
"text" => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
|
||||
];
|
||||
$response = Http::withHeaders([
|
||||
'content-type' => 'applications/json',
|
||||
'content-type' => 'application/json',
|
||||
])->post($this->webhook_endpoint,
|
||||
$payload)->throw();
|
||||
}
|
||||
@@ -269,7 +269,7 @@ class SlackSettingsForm extends Component
|
||||
$notification->success()->sendMessage($message);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'content-type' => 'applications/json',
|
||||
'content-type' => 'application/json',
|
||||
])->post($this->webhook_endpoint);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class CheckinAccessoryMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -43,7 +43,7 @@ class CheckinAssetMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -34,7 +34,7 @@ class CheckinLicenseMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -21,7 +21,7 @@ class CheckoutAccessoryMail extends Mailable
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy,$note, $acceptance)
|
||||
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
{
|
||||
$this->item = $accessory;
|
||||
$this->admin = $checkedOutBy;
|
||||
@@ -37,7 +37,7 @@ class CheckoutAccessoryMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -23,7 +23,7 @@ class CheckoutAssetMail extends Mailable
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note, $acceptance)
|
||||
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
|
||||
{
|
||||
$this->item = $asset;
|
||||
$this->admin = $checkedOutBy;
|
||||
@@ -52,7 +52,7 @@ class CheckoutAssetMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR', 'service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -38,7 +38,7 @@ class CheckoutConsumableMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
@@ -27,7 +27,6 @@ class CheckoutLicenseMail extends Mailable
|
||||
$this->note = $note;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ class CheckoutLicenseMail extends Mailable
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
||||
67
app/Mail/UnacceptedAssetReminderMail.php
Normal file
67
app/Mail/UnacceptedAssetReminderMail.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class UnacceptedAssetReminderMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($checkout_info, $count)
|
||||
{
|
||||
$this->count = $count;
|
||||
$this->target = $checkout_info['acceptance']?->assignedTo;
|
||||
$this->acceptance = $checkout_info['acceptance'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(config('mail.from.address'), config('mail.from.name'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans('mail.unaccepted_asset_reminder'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
$accept_url = route('account.accept');
|
||||
|
||||
return new Content(
|
||||
markdown: 'notifications.markdown.asset-reminder',
|
||||
with: [
|
||||
'count' => $this->count,
|
||||
'assigned_to' => $this->target?->present()->fullName,
|
||||
'link' => route('account.accept'),
|
||||
'accept_url' => $accept_url,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,14 @@ class AccessoryCheckout extends Model
|
||||
{
|
||||
use Searchable;
|
||||
|
||||
protected $fillable = ['created_by', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
|
||||
protected $fillable = [
|
||||
'accessory_id',
|
||||
'assigned_to',
|
||||
'assigned_type',
|
||||
'note'
|
||||
];
|
||||
|
||||
protected $presenter = \App\Presenters\AccessoryPresenter::class;
|
||||
protected $table = 'accessories_checkout';
|
||||
|
||||
/**
|
||||
@@ -34,9 +41,13 @@ class AccessoryCheckout extends Model
|
||||
*/
|
||||
public function accessory()
|
||||
{
|
||||
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
|
||||
return $this->hasOne(Accessory::class, 'id', 'accessory_id');
|
||||
}
|
||||
|
||||
public function accessories()
|
||||
{
|
||||
return $this->hasMany(Accessory::class, 'id', 'accessory_id');
|
||||
}
|
||||
/**
|
||||
* Establishes the accessory checkout -> user relationship
|
||||
*
|
||||
@@ -44,9 +55,9 @@ class AccessoryCheckout extends Model
|
||||
* @since [v7.0.9]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function user()
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->hasOne(\App\Models\User::class, 'user_id');
|
||||
return $this->hasOne(\App\Models\User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +87,7 @@ class AccessoryCheckout extends Model
|
||||
/**
|
||||
* Determines whether the accessory is checked out to a user
|
||||
*
|
||||
* Even though we allow allow for checkout to things beyond users
|
||||
* Even though we allow for checkout to things beyond users
|
||||
* this method is an easy way of seeing if we are checked out to a user.
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
@@ -84,7 +95,17 @@ class AccessoryCheckout extends Model
|
||||
*/
|
||||
public function checkedOutToUser(): bool
|
||||
{
|
||||
return $this->assignedType() === Asset::USER;
|
||||
return $this->assigned_type == User::class;
|
||||
}
|
||||
|
||||
public function checkedOutToLocation(): bool
|
||||
{
|
||||
return $this->assigned_type == Location::class;
|
||||
}
|
||||
|
||||
public function checkedOutToAsset(): bool
|
||||
{
|
||||
return $this->assigned_type == Asset::class;
|
||||
}
|
||||
|
||||
public function scopeUserAssigned(Builder $query): void
|
||||
@@ -92,6 +113,16 @@ class AccessoryCheckout extends Model
|
||||
$query->where('assigned_type', '=', User::class);
|
||||
}
|
||||
|
||||
public function scopeLocationAssigned(Builder $query): void
|
||||
{
|
||||
$query->where('assigned_type', '=', Location::class);
|
||||
}
|
||||
|
||||
public function scopeAssetAssigned(Builder $query): void
|
||||
{
|
||||
$query->where('assigned_type', '=', Asset::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
*
|
||||
|
||||
@@ -69,7 +69,7 @@ class Actionlog extends SnipeModel
|
||||
'company' => ['name'],
|
||||
'adminuser' => ['first_name','last_name','username', 'email'],
|
||||
'user' => ['first_name','last_name','username', 'email'],
|
||||
'assets' => ['asset_tag','name'],
|
||||
'assets' => ['asset_tag','name', 'serial'],
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ class CheckoutAcceptance extends Model
|
||||
/**
|
||||
* The resource that was is out
|
||||
*
|
||||
* @return Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function checkoutable()
|
||||
{
|
||||
|
||||
@@ -211,10 +211,15 @@ final class Company extends SnipeModel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->hasMany(User::class, 'company_id');
|
||||
public function users() {
|
||||
return $this->hasManyThrough(
|
||||
User::class,
|
||||
UserCompany::class,
|
||||
'path_id',
|
||||
'course_id',
|
||||
'id',
|
||||
'course_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function assets()
|
||||
@@ -249,11 +254,6 @@ final class Company extends SnipeModel
|
||||
/**
|
||||
* Scoping table queries, determining if a logged in user is part of a company, and only allows the user to access items associated with that company if FMCS is enabled.
|
||||
*
|
||||
* This method is the one that the CompanyableTrait uses to contrain queries automatically, however that trait CANNOT be
|
||||
* applied to the user's model, since it causes an infinite loop against the authenticated user.
|
||||
*
|
||||
* @todo - refactor that trait to handle the user's model as well.
|
||||
*
|
||||
* @author [A. Gianotto] <snipe@snipe.net>
|
||||
* @param $query
|
||||
* @param $column
|
||||
@@ -279,11 +279,13 @@ final class Company extends SnipeModel
|
||||
private static function scopeCompanyablesDirectly($query, $column = 'company_id', $table_name = null)
|
||||
{
|
||||
|
||||
// Get the company ID of the logged-in user, or set it to null if there is no company associated with the user
|
||||
// Get the company IDs of the logged-in user, or set it to null if there is no company associated with the user
|
||||
if (Auth::hasUser()) {
|
||||
$company_id = auth()->user()->company_id;
|
||||
$companies = auth()->user()->companies()->pluck('companies.id');
|
||||
\Log::debug(auth()->user()->id);
|
||||
\Log::debug(print_r($companies, true));
|
||||
} else {
|
||||
$company_id = null;
|
||||
$companies = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -293,7 +295,7 @@ final class Company extends SnipeModel
|
||||
// Dynamically get the table name if it's not passed in, based on the model we're querying against
|
||||
$table = ($table_name) ? $table_name."." : $query->getModel()->getTable().".";
|
||||
|
||||
return $query->where($table.$column, '=', $company_id);
|
||||
return $query->whereIn($table.$column, $companies);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Rules\AlphaEncrypted;
|
||||
use App\Rules\NumericEncrypted;
|
||||
use Gate;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -95,6 +97,19 @@ class CustomFieldset extends Model
|
||||
array_push($rule, $field->attributes['format']);
|
||||
$rules[$field->db_column_name()] = $rule;
|
||||
|
||||
|
||||
// these are to replace the standard 'numeric' and 'alpha' rules if the custom field is also encrypted.
|
||||
// the values need to be decrypted first, because encrypted strings are alphanumeric
|
||||
if ($field->format === 'NUMERIC' && $field->field_encrypted) {
|
||||
$numericKey = array_search('numeric', $rules[$field->db_column_name()]);
|
||||
$rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
|
||||
}
|
||||
|
||||
if ($field->format === 'ALPHA' && $field->field_encrypted) {
|
||||
$alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
|
||||
$rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
|
||||
}
|
||||
|
||||
// add not_array to rules for all fields but checkboxes
|
||||
if ($field->element != 'checkbox') {
|
||||
$rules[$field->db_column_name()][] = 'not_array';
|
||||
|
||||
@@ -141,4 +141,17 @@ class Department extends SnipeModel
|
||||
{
|
||||
return $query->leftJoin('users as department_user', 'departments.manager_id', '=', 'department_user.id')->orderBy('department_user.first_name', $order)->orderBy('department_user.last_name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderCompany($query, $order)
|
||||
{
|
||||
return $query->leftJoin('companies as company_sort', 'departments.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order);
|
||||
}
|
||||
}
|
||||
|
||||
107
app/Models/Labels/Tapes/Brother/TZe_24mm_D.php
Normal file
107
app/Models/Labels/Tapes/Brother/TZe_24mm_D.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Brother;
|
||||
|
||||
class TZe_24mm_D extends TZe_24mm
|
||||
{
|
||||
private const BARCODE_MARGIN = 1.40;
|
||||
private const TAG_SIZE = 2.80;
|
||||
private const TITLE_SIZE = 2.80;
|
||||
private const TITLE_MARGIN = 0.50;
|
||||
private const LABEL_SIZE = 2.50;
|
||||
private const LABEL_MARGIN = - 0.35;
|
||||
private const FIELD_SIZE = 2.50;
|
||||
private const FIELD_MARGIN = 0.35;
|
||||
private const BARCODE1D_SIZE = 3.00; // Size for the C128 barcode at bottom
|
||||
|
||||
public function getUnit() { return 'mm'; }
|
||||
public function getWidth() { return 65.0; }
|
||||
public function getSupportAssetTag() { return true; }
|
||||
public function getSupport1DBarcode() { return true; }
|
||||
public function getSupport2DBarcode() { return true; }
|
||||
public function getSupportFields() { return 3; }
|
||||
public function getSupportLogo() { return false; }
|
||||
public function getSupportTitle() { return true; }
|
||||
|
||||
public function preparePDF($pdf) {}
|
||||
|
||||
public function write($pdf, $record) {
|
||||
$pa = $this->getPrintableArea();
|
||||
|
||||
$currentX = $pa->x1;
|
||||
$currentY = $pa->y1;
|
||||
$usableWidth = $pa->w;
|
||||
|
||||
// Reserve space at bottom for 1D barcode
|
||||
$usableHeight = $pa->h - self::BARCODE1D_SIZE;
|
||||
$barcodeSize = $usableHeight - self::TAG_SIZE;
|
||||
|
||||
if ($record->has('barcode2d')) {
|
||||
static::writeText(
|
||||
$pdf, $record->get('tag'),
|
||||
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
|
||||
'freemono', 'b', self::TAG_SIZE, 'C',
|
||||
$barcodeSize, self::TAG_SIZE, true, 0
|
||||
);
|
||||
static::write2DBarcode(
|
||||
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
|
||||
$currentX, $currentY,
|
||||
$barcodeSize, $barcodeSize
|
||||
);
|
||||
$currentX += $barcodeSize + self::BARCODE_MARGIN;
|
||||
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
|
||||
} else {
|
||||
static::writeText(
|
||||
$pdf, $record->get('tag'),
|
||||
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
|
||||
'freemono', 'b', self::TAG_SIZE, 'R',
|
||||
$usableWidth, self::TAG_SIZE, true, 0
|
||||
);
|
||||
}
|
||||
|
||||
if ($record->has('title')) {
|
||||
static::writeText(
|
||||
$pdf, $record->get('title'),
|
||||
$currentX, $currentY,
|
||||
'freesans', '', self::TITLE_SIZE, 'L',
|
||||
$usableWidth, self::TITLE_SIZE, true, 0
|
||||
);
|
||||
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
|
||||
}
|
||||
|
||||
foreach ($record->get('fields') as $field) {
|
||||
// Write label and value on the same line
|
||||
// Calculate label width with proportional character spacing
|
||||
$labelWidth = $pdf->GetStringWidth($field['label'], 'freemono', '', self::LABEL_SIZE);
|
||||
$charCount = strlen($field['label']);
|
||||
$spacingPerChar = 0.5;
|
||||
$totalSpacing = $charCount * $spacingPerChar;
|
||||
$adjustedWidth = $labelWidth + $totalSpacing;
|
||||
|
||||
static::writeText(
|
||||
$pdf, $field['label'],
|
||||
$currentX, $currentY,
|
||||
'freemono', 'B', self::LABEL_SIZE, 'L',
|
||||
$adjustedWidth, self::LABEL_SIZE, true, 0, $spacingPerChar
|
||||
);
|
||||
|
||||
static::writeText(
|
||||
$pdf, $field['value'],
|
||||
$currentX + $adjustedWidth + 2, $currentY,
|
||||
'freemono', 'B', self::FIELD_SIZE, 'L',
|
||||
$usableWidth - $adjustedWidth - 2, self::FIELD_SIZE, true, 0, 0.3
|
||||
);
|
||||
|
||||
$currentY += max(self::LABEL_SIZE, self::FIELD_SIZE) + self::FIELD_MARGIN;
|
||||
}
|
||||
|
||||
// Add C128 barcode at the bottom
|
||||
if ($record->has('barcode1d')) {
|
||||
static::write1DBarcode(
|
||||
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
|
||||
$pa->x1, $pa->y2 - self::BARCODE1D_SIZE,
|
||||
$pa->w, self::BARCODE1D_SIZE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +49,7 @@ class LabelWriter_1933081 extends LabelWriter
|
||||
);
|
||||
$currentX += $barcodeSize + self::BARCODE_MARGIN;
|
||||
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
|
||||
} else {
|
||||
static::writeText(
|
||||
$pdf, $record->get('tag'),
|
||||
$pa->x1, $pa->y2 - self::TAG_SIZE,
|
||||
'freesans', 'b', self::TAG_SIZE, 'R',
|
||||
$usableWidth, self::TAG_SIZE, true, 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($record->has('title')) {
|
||||
static::writeText(
|
||||
|
||||
@@ -49,13 +49,6 @@ class LabelWriter_2112283 extends LabelWriter
|
||||
);
|
||||
$currentX += $barcodeSize + self::BARCODE_MARGIN;
|
||||
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
|
||||
} else {
|
||||
static::writeText(
|
||||
$pdf, $record->get('tag'),
|
||||
$pa->x1, $pa->y2 - self::TAG_SIZE,
|
||||
'freesans', 'b', self::TAG_SIZE, 'R',
|
||||
$usableWidth, self::TAG_SIZE, true, 0
|
||||
);
|
||||
}
|
||||
|
||||
if ($record->has('title')) {
|
||||
|
||||
@@ -50,13 +50,6 @@ class LabelWriter_30252 extends LabelWriter
|
||||
);
|
||||
$currentX += $barcodeSize + self::BARCODE_MARGIN;
|
||||
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
|
||||
} else {
|
||||
static::writeText(
|
||||
$pdf, $record->get('tag'),
|
||||
$pa->x1, $pa->y2 - self::TAG_SIZE,
|
||||
'freemono', 'b', self::TAG_SIZE, 'R',
|
||||
$usableWidth, self::TAG_SIZE, true, 0
|
||||
);
|
||||
}
|
||||
|
||||
if ($record->has('title')) {
|
||||
|
||||
@@ -253,6 +253,18 @@ class Location extends SnipeModel
|
||||
return $this->morphMany(\App\Models\Asset::class, 'assigned', 'assigned_type', 'assigned_to')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the accessory -> location assignment relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assignedAccessories()
|
||||
{
|
||||
return $this->morphMany(\App\Models\AccessoryCheckout::class, 'assigned', 'assigned_type', 'assigned_to');
|
||||
}
|
||||
|
||||
public function setLdapOuAttribute($ldap_ou)
|
||||
{
|
||||
return $this->attributes['ldap_ou'] = empty($ldap_ou) ? null : $ldap_ou;
|
||||
|
||||
241
app/Models/ReportTemplate.php
Normal file
241
app/Models/ReportTemplate.php
Normal file
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class ReportTemplate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
use ValidatingTrait;
|
||||
|
||||
protected $casts = [
|
||||
'options' => 'array',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'created_by',
|
||||
'name',
|
||||
'options',
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
'options' => [
|
||||
'required',
|
||||
'array',
|
||||
],
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
// Scope to current user
|
||||
static::addGlobalScope('current_user', function (Builder $builder) {
|
||||
if (auth()->check()) {
|
||||
$builder->where('created_by', auth()->id());
|
||||
}
|
||||
});
|
||||
|
||||
static::created(function (ReportTemplate $reportTemplate) {
|
||||
$logAction = new Actionlog([
|
||||
'item_type' => ReportTemplate::class,
|
||||
'item_id' => $reportTemplate->id,
|
||||
'created_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
$logAction->logaction('create');
|
||||
});
|
||||
|
||||
static::updated(function (ReportTemplate $reportTemplate) {
|
||||
$changed = [];
|
||||
|
||||
foreach ($reportTemplate->getDirty() as $key => $value) {
|
||||
$changed[$key] = [
|
||||
'old' => $reportTemplate->getOriginal($key),
|
||||
'new' => $reportTemplate->getAttribute($key),
|
||||
];
|
||||
}
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = ReportTemplate::class;
|
||||
$logAction->item_id = $reportTemplate->id;
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
$logAction->logaction('update');
|
||||
});
|
||||
|
||||
static::deleted(function (ReportTemplate $reportTemplate) {
|
||||
$logAction = new Actionlog([
|
||||
'item_type' => ReportTemplate::class,
|
||||
'item_id' => $reportTemplate->id,
|
||||
'created_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
$logAction->logaction('delete');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the report template -> creator relationship.
|
||||
*
|
||||
*/
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a checkbox field for the given field name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string $fallbackValue The value to return if the report template is not saved yet.
|
||||
*
|
||||
*/
|
||||
public function checkmarkValue(string $fieldName, string $fallbackValue = '1'): string
|
||||
{
|
||||
// Assuming we're using the null object pattern, and an empty model
|
||||
// was passed to the view when showing the default report page,
|
||||
// return the fallback value so that checkboxes are checked by default.
|
||||
if (is_null($this->id)) {
|
||||
return $fallbackValue;
|
||||
}
|
||||
|
||||
// If the model does exist then return the value of the field
|
||||
// or return 0 so the checkbox is unchecked.
|
||||
// Falling back to 0 here is because checkboxes are not sent
|
||||
// in the request when unchecked so they are not
|
||||
// actually saved in the model's options.
|
||||
return $this->options[$fieldName] ?? '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a radio field for the given field name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string $value The value to check against.
|
||||
* @param bool $isDefault Whether the radio input being checked is the default.
|
||||
*
|
||||
*/
|
||||
public function radioValue(string $fieldName, string $value, bool $isDefault = false): bool
|
||||
{
|
||||
$fieldExists = array_has($this->options, $fieldName);
|
||||
|
||||
// If the field doesn't exist but the radio input
|
||||
// being checked is the default then return true.
|
||||
if (!$fieldExists && $isDefault) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the field exists and matches what we're checking then return true.
|
||||
if ($fieldExists && $this->options[$fieldName] === $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a select field for the given field name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string|null $model The Eloquent model to check against.
|
||||
*
|
||||
* @return mixed|null
|
||||
*
|
||||
*/
|
||||
public function selectValue(string $fieldName, string $model = null)
|
||||
{
|
||||
// If the field does not exist then return null.
|
||||
if (!isset($this->options[$fieldName])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $this->options[$fieldName];
|
||||
|
||||
// If the value was stored as an array, most likely
|
||||
// due to a previously being a multi-select,
|
||||
// then return the first value.
|
||||
if (is_array($value)) {
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
// If a model is provided then we should ensure we only return
|
||||
// the value if the model still exists.
|
||||
// Note: It is possible $value is an id that no longer exists and this will return null.
|
||||
if ($model) {
|
||||
$foundModel = $model::find($value);
|
||||
|
||||
return $foundModel ? $foundModel->id : null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values of a multi-select field for the given field name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string|null $model The Eloquent model to check against.
|
||||
*
|
||||
* @return iterable
|
||||
*
|
||||
*/
|
||||
public function selectValues(string $fieldName, string $model = null): iterable
|
||||
{
|
||||
// If the field does not exist then return an empty array.
|
||||
if (!isset($this->options[$fieldName])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If a model is provided then we should ensure we only return
|
||||
// the ids of models that exist and are not deleted.
|
||||
if ($model) {
|
||||
return $model::findMany($this->options[$fieldName])->pluck('id');
|
||||
}
|
||||
|
||||
// Wrap the value in an array if needed. This is to ensure
|
||||
// values previously stored as a single value,
|
||||
// most likely from a single select, are returned as an array.
|
||||
if (!is_array($this->options[$fieldName])) {
|
||||
return [$this->options[$fieldName]];
|
||||
}
|
||||
|
||||
return $this->options[$fieldName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a text field for the given field name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string|null $fallbackValue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function textValue(string $fieldName, string|null $fallbackValue = ''): string
|
||||
{
|
||||
// Assuming we're using the null object pattern,
|
||||
// return the default value if the object is not saved yet.
|
||||
if (is_null($this->id)) {
|
||||
return (string) $fallbackValue;
|
||||
}
|
||||
|
||||
// Return the field's value if it exists
|
||||
// and return the default value if not.
|
||||
return $this->options[$fieldName] ?? '';
|
||||
}
|
||||
|
||||
public function getDisplayNameAttribute()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user