Compare commits
510 Commits
v6.2.0
...
features/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
480091f47e | ||
|
|
1e150e2683 | ||
|
|
5730518fc6 | ||
|
|
fb1965331c | ||
|
|
fdaa44654d | ||
|
|
1455331a90 | ||
|
|
42109dc1eb | ||
|
|
47c8ae000a | ||
|
|
5a60df55d1 | ||
|
|
16da994e28 | ||
|
|
2a00dc2d28 | ||
|
|
4a19f23e7c | ||
|
|
5b4d6b346b | ||
|
|
96210e9498 | ||
|
|
3152df2c48 | ||
|
|
189c90e9e5 | ||
|
|
c35d234cde | ||
|
|
80fafeaebe | ||
|
|
63065bab5d | ||
|
|
108658520d | ||
|
|
9e59550b79 | ||
|
|
899890b224 | ||
|
|
7ac3341b4f | ||
|
|
9d5ceb685d | ||
|
|
601ebcc1e6 | ||
|
|
2247be77d8 | ||
|
|
2d4a14d4bb | ||
|
|
3b9f069627 | ||
|
|
2c6b957fbe | ||
|
|
6ae03a204b | ||
|
|
187d3abeb4 | ||
|
|
c91713e20a | ||
|
|
fa1176ce14 | ||
|
|
c71726c83e | ||
|
|
ca430ec9b3 | ||
|
|
9d786d9386 | ||
|
|
4723cfd4ba | ||
|
|
f9d5c451bc | ||
|
|
5574218966 | ||
|
|
c997ae44c2 | ||
|
|
77ceac5747 | ||
|
|
990358750b | ||
|
|
72dbe95168 | ||
|
|
37bd297094 | ||
|
|
5d32956070 | ||
|
|
45e9d0597a | ||
|
|
8e8b1068ff | ||
|
|
8c7edcb357 | ||
|
|
dbb6002f9f | ||
|
|
776c91934b | ||
|
|
77ab5eb875 | ||
|
|
eca13d0593 | ||
|
|
9607567b75 | ||
|
|
1217bff4b6 | ||
|
|
ea55daf913 | ||
|
|
0ff99f0479 | ||
|
|
8916c976e1 | ||
|
|
7b12d511f8 | ||
|
|
81b2273c37 | ||
|
|
978bbeccc5 | ||
|
|
accfbc1b99 | ||
|
|
cf3608ae10 | ||
|
|
aaa6a31eb3 | ||
|
|
88cc234cbd | ||
|
|
94eeca7429 | ||
|
|
e0e11845bb | ||
|
|
a7f98ea2a5 | ||
|
|
8e3657d62e | ||
|
|
ddd7f206d3 | ||
|
|
81cf08adf5 | ||
|
|
1b77632ecb | ||
|
|
d4bdaf16e7 | ||
|
|
e3a8952f4b | ||
|
|
ad6b286e10 | ||
|
|
7f94b7df32 | ||
|
|
6409018b08 | ||
|
|
31f429e1c4 | ||
|
|
3a0b994443 | ||
|
|
1307ef19cf | ||
|
|
aed6b531cb | ||
|
|
bb0ba0bebe | ||
|
|
ba127be344 | ||
|
|
c6178bd619 | ||
|
|
3929c8f260 | ||
|
|
f922d0518e | ||
|
|
45d9119733 | ||
|
|
e269415fec | ||
|
|
8481768c3d | ||
|
|
d3175b2ca4 | ||
|
|
a8123db4fb | ||
|
|
b1e782d2f0 | ||
|
|
60517f811e | ||
|
|
58679a13eb | ||
|
|
3d9a9c7fe8 | ||
|
|
22246dd099 | ||
|
|
a7f76f9860 | ||
|
|
8b79a8afdf | ||
|
|
299db76009 | ||
|
|
20c0f687e9 | ||
|
|
d06cfe6502 | ||
|
|
534ac5ac53 | ||
|
|
e2d0136336 | ||
|
|
6a3ab526de | ||
|
|
19877244cd | ||
|
|
f7ccef16e7 | ||
|
|
0e51a0935d | ||
|
|
ffc1266d0e | ||
|
|
2972c31a11 | ||
|
|
0d29ac9856 | ||
|
|
eae98d3f35 | ||
|
|
a08dcbdfdb | ||
|
|
9c4fbbe915 | ||
|
|
d6a337a584 | ||
|
|
83ee07cca1 | ||
|
|
9502525a41 | ||
|
|
68ac1aaae0 | ||
|
|
6a2ab2cfb2 | ||
|
|
fb125af0df | ||
|
|
590cd0c71f | ||
|
|
0ae91d305d | ||
|
|
9347af0970 | ||
|
|
766eee78b2 | ||
|
|
2b9e4110bc | ||
|
|
4eb435e148 | ||
|
|
e5a4214b8b | ||
|
|
a203189724 | ||
|
|
b02c60c2ce | ||
|
|
c14b3d9780 | ||
|
|
01fec1d6bb | ||
|
|
01740b7969 | ||
|
|
7c88601066 | ||
|
|
fe2cfa0d38 | ||
|
|
2c62a7e78d | ||
|
|
ad6822875d | ||
|
|
5569c2ba4a | ||
|
|
2f12a9cfe2 | ||
|
|
81583207a9 | ||
|
|
b1f75a3bb3 | ||
|
|
c7f90ad86d | ||
|
|
0924a53789 | ||
|
|
eb87720661 | ||
|
|
9316ecb8d6 | ||
|
|
a81e2be6da | ||
|
|
73ebd9017f | ||
|
|
4546e87eb5 | ||
|
|
9dc9834bcb | ||
|
|
4ecce51b57 | ||
|
|
000a28d648 | ||
|
|
495df737fc | ||
|
|
0bae1a1f5e | ||
|
|
9bff2d0bbf | ||
|
|
9e1cfac995 | ||
|
|
7d30001ff8 | ||
|
|
49136a4d67 | ||
|
|
4382adce85 | ||
|
|
2051ac785d | ||
|
|
9cc89911f7 | ||
|
|
495521b70e | ||
|
|
6da0671143 | ||
|
|
a24d21e683 | ||
|
|
d2bf71327b | ||
|
|
ef10f2e50e | ||
|
|
08153d418f | ||
|
|
3f8b63e053 | ||
|
|
d2bfa9a3b6 | ||
|
|
26311e46b8 | ||
|
|
b3a6cc633b | ||
|
|
bc9545ca96 | ||
|
|
cf42eca198 | ||
|
|
44d064f094 | ||
|
|
3fb913669b | ||
|
|
1171141904 | ||
|
|
eceb69122c | ||
|
|
154e71de74 | ||
|
|
93101d36b8 | ||
|
|
4fb349e326 | ||
|
|
4d2790c3f4 | ||
|
|
a08e0bd547 | ||
|
|
92c7de3fb5 | ||
|
|
d0cc42a068 | ||
|
|
8c316e0bd3 | ||
|
|
7a638e4a65 | ||
|
|
3b6a0d6525 | ||
|
|
4d65d09306 | ||
|
|
c5e1cdbcee | ||
|
|
75532d9662 | ||
|
|
13d3f85c62 | ||
|
|
938ec75aa7 | ||
|
|
c9604b896a | ||
|
|
d167ec6dc0 | ||
|
|
e05af5216e | ||
|
|
3f834cb88f | ||
|
|
d971172cf3 | ||
|
|
53bd5626c9 | ||
|
|
8f75bb8706 | ||
|
|
b67b00dd82 | ||
|
|
d4b9f6a2a4 | ||
|
|
5073c3389f | ||
|
|
f822f8b186 | ||
|
|
e381bf978f | ||
|
|
95e9002cd0 | ||
|
|
22996895d8 | ||
|
|
9f9f8c173b | ||
|
|
e6810e8db9 | ||
|
|
28641493ca | ||
|
|
9a7e7bddcc | ||
|
|
aa2a25dc19 | ||
|
|
57372fc82a | ||
|
|
ae90f9defa | ||
|
|
3495652827 | ||
|
|
5779a63221 | ||
|
|
209627e2cc | ||
|
|
9fc1cbd7ae | ||
|
|
e405e27643 | ||
|
|
96211cf73c | ||
|
|
79f380cbe7 | ||
|
|
518c43680d | ||
|
|
e554a80589 | ||
|
|
f47a8664f9 | ||
|
|
97953ae082 | ||
|
|
8e20071254 | ||
|
|
316c90c144 | ||
|
|
462d2d329a | ||
|
|
a6b713ae21 | ||
|
|
0d67ae27c2 | ||
|
|
bd894677ef | ||
|
|
d404452f5a | ||
|
|
8c46f4ef69 | ||
|
|
740569791f | ||
|
|
90eb3b86cf | ||
|
|
10abecc5a6 | ||
|
|
f12d44509e | ||
|
|
91b53bffa1 | ||
|
|
a2ba39c837 | ||
|
|
aab7eb4a85 | ||
|
|
7b2f49644e | ||
|
|
305cd9b0b8 | ||
|
|
9bba0b764a | ||
|
|
1ce95b6eac | ||
|
|
9c90f03142 | ||
|
|
b4d3f51414 | ||
|
|
64219da0b4 | ||
|
|
7b9259274c | ||
|
|
c1213e0abc | ||
|
|
05de8a5731 | ||
|
|
ff72c4fbaa | ||
|
|
9d887484c8 | ||
|
|
c4c47f2e8d | ||
|
|
0cebccac95 | ||
|
|
c3b3aa4de6 | ||
|
|
3717d60170 | ||
|
|
6c996ac8fa | ||
|
|
7042260871 | ||
|
|
2695576968 | ||
|
|
f25ddee857 | ||
|
|
541ba0c0ba | ||
|
|
77d141d19c | ||
|
|
436192b836 | ||
|
|
522aa96fcc | ||
|
|
b87879f8e7 | ||
|
|
60e0e899bc | ||
|
|
6ebc01ca50 | ||
|
|
4b6b36c639 | ||
|
|
4a759f0a20 | ||
|
|
ca1845efd7 | ||
|
|
7ed58a177b | ||
|
|
d93e399295 | ||
|
|
1b614c13fb | ||
|
|
56fd41eee4 | ||
|
|
33b45898b1 | ||
|
|
7509d57bf6 | ||
|
|
da08f0e26f | ||
|
|
0feb56d931 | ||
|
|
a0cae77278 | ||
|
|
0f97c0601b | ||
|
|
3916bac95f | ||
|
|
6264d90459 | ||
|
|
57d3b2d2ef | ||
|
|
a202bf81fa | ||
|
|
43da292d60 | ||
|
|
ab3edae0b6 | ||
|
|
887d013a39 | ||
|
|
eb82bc290d | ||
|
|
8b9b46c562 | ||
|
|
307f84dff8 | ||
|
|
3c6b79e807 | ||
|
|
e5bd78a722 | ||
|
|
23a6547ada | ||
|
|
63a86c5622 | ||
|
|
933ce61efd | ||
|
|
88661a58a7 | ||
|
|
08693f09b0 | ||
|
|
3a37b80251 | ||
|
|
e7ef7f04f0 | ||
|
|
c4adc3ccc8 | ||
|
|
96f7f49b68 | ||
|
|
8c93e79397 | ||
|
|
4d345168d6 | ||
|
|
f2f3931fa9 | ||
|
|
c919cd31fa | ||
|
|
9d2d13bb59 | ||
|
|
944520bc5d | ||
|
|
aae289e150 | ||
|
|
c756849497 | ||
|
|
29f438123e | ||
|
|
0c5d54dfde | ||
|
|
49f6eef9df | ||
|
|
8f8e5cb723 | ||
|
|
d5598b3322 | ||
|
|
309f30f630 | ||
|
|
04a867d12b | ||
|
|
e62636b3a5 | ||
|
|
4e20a241ce | ||
|
|
30cc498a16 | ||
|
|
5ae09b791f | ||
|
|
bd195a6911 | ||
|
|
58062ff9f5 | ||
|
|
7571d45d44 | ||
|
|
47186b0abe | ||
|
|
e0a4387b10 | ||
|
|
38066bf162 | ||
|
|
6b745930b5 | ||
|
|
38de69b3da | ||
|
|
bc6f014fff | ||
|
|
1be503b21a | ||
|
|
825c800d32 | ||
|
|
0c86b67ed6 | ||
|
|
4d841dbc79 | ||
|
|
f2fb5b43e3 | ||
|
|
3747165f25 | ||
|
|
787a64d313 | ||
|
|
f7bb911b99 | ||
|
|
388e4c10c4 | ||
|
|
74ed32903f | ||
|
|
f2bc7d6173 | ||
|
|
72fe94edb3 | ||
|
|
3e54a9d561 | ||
|
|
4bdd070f7b | ||
|
|
277608d962 | ||
|
|
82c2a46ada | ||
|
|
3f6554ee34 | ||
|
|
1e0512ad44 | ||
|
|
23fa92f4ce | ||
|
|
2218cff7bc | ||
|
|
b1f158ba5a | ||
|
|
a92a9d7616 | ||
|
|
8f23a45cb3 | ||
|
|
93cccf4f5f | ||
|
|
04f6f39588 | ||
|
|
ca1420c9bd | ||
|
|
2537d0fdaf | ||
|
|
b2aed7feea | ||
|
|
ea960c39bb | ||
|
|
0b39591d88 | ||
|
|
c14a01eb8b | ||
|
|
b96e77268a | ||
|
|
f3bd23da3d | ||
|
|
9b53b0fedc | ||
|
|
138ec33555 | ||
|
|
e99d307350 | ||
|
|
417f9c21e4 | ||
|
|
43b9e6401c | ||
|
|
dae9e6d096 | ||
|
|
9ef598d07b | ||
|
|
2a29566458 | ||
|
|
ab3a3de59b | ||
|
|
47a77eabf2 | ||
|
|
758d3aadb4 | ||
|
|
f685ba01b6 | ||
|
|
e3591dc756 | ||
|
|
0fbe63d0cf | ||
|
|
b9dc7f88d0 | ||
|
|
f6527e0b42 | ||
|
|
866bbe5e11 | ||
|
|
6d55d78280 | ||
|
|
7a0d3f788f | ||
|
|
e5f5802235 | ||
|
|
d2dc3253ab | ||
|
|
5277f7cc5c | ||
|
|
25a5507d9d | ||
|
|
890efb18d8 | ||
|
|
eea2eabaee | ||
|
|
cd3b4754e8 | ||
|
|
1dab4b59e9 | ||
|
|
05dd2b4008 | ||
|
|
b809625856 | ||
|
|
8d921359ca | ||
|
|
c07c0c0f74 | ||
|
|
cc82e86eeb | ||
|
|
8c9961aba8 | ||
|
|
46af40bd02 | ||
|
|
6cda9056b8 | ||
|
|
101bd9c404 | ||
|
|
ad6b502005 | ||
|
|
cb10c7af27 | ||
|
|
99d409c0a5 | ||
|
|
37c1d6fc04 | ||
|
|
c69958d95d | ||
|
|
92776adb93 | ||
|
|
dee36fc294 | ||
|
|
a2773aa895 | ||
|
|
e82fec2a5f | ||
|
|
db4c86a4f4 | ||
|
|
1abcc8b802 | ||
|
|
724c054838 | ||
|
|
1e8f90bcf0 | ||
|
|
129e9b90dc | ||
|
|
c7b8880d69 | ||
|
|
228f21ed91 | ||
|
|
b7c390a257 | ||
|
|
758d808772 | ||
|
|
754ec7563a | ||
|
|
0184d25a95 | ||
|
|
bed1055c4e | ||
|
|
41eccaeae0 | ||
|
|
eb2edb7475 | ||
|
|
ed77e8768c | ||
|
|
3aa256ec02 | ||
|
|
c1f545a523 | ||
|
|
8792d654b6 | ||
|
|
f8730adb11 | ||
|
|
36638feac4 | ||
|
|
4675f02349 | ||
|
|
c21586dee5 | ||
|
|
f16e81e0e8 | ||
|
|
3b4004ac18 | ||
|
|
0bf423cda9 | ||
|
|
a54fab5c33 | ||
|
|
96c6c93197 | ||
|
|
4d67c72eea | ||
|
|
6fa0d42bc2 | ||
|
|
7512400b56 | ||
|
|
1acb14a39f | ||
|
|
bb7a41628a | ||
|
|
402ff58628 | ||
|
|
492fb15036 | ||
|
|
cc4cb14e9d | ||
|
|
ee72c92d4f | ||
|
|
a47e36c1e5 | ||
|
|
a4a25ab9d0 | ||
|
|
518ae28a37 | ||
|
|
74a891afbb | ||
|
|
048ad57418 | ||
|
|
7bcb28d8fd | ||
|
|
660abeca9e | ||
|
|
1509339b68 | ||
|
|
81ae32d3f9 | ||
|
|
279e6c7e4f | ||
|
|
81a982fd77 | ||
|
|
619712b927 | ||
|
|
359cd2a267 | ||
|
|
456d55c3bd | ||
|
|
5a88a64ebd | ||
|
|
b1199100a0 | ||
|
|
b7901ae2d8 | ||
|
|
4660a2e5b7 | ||
|
|
a9123754f5 | ||
|
|
cbef531811 | ||
|
|
7dab59c98d | ||
|
|
c48a47936c | ||
|
|
66abf8d5c0 | ||
|
|
2ace24b176 | ||
|
|
d65d1930e4 | ||
|
|
2d59517c35 | ||
|
|
3cb906a05f | ||
|
|
6b4f8f1813 | ||
|
|
929e107a20 | ||
|
|
1cfd7673e0 | ||
|
|
afe9d43139 | ||
|
|
aac627592b | ||
|
|
f35f9f922e | ||
|
|
44441ec703 | ||
|
|
7c9a4ac161 | ||
|
|
64a9859efd | ||
|
|
ef64843d2f | ||
|
|
0368b9df43 | ||
|
|
951521dc81 | ||
|
|
70a251de55 | ||
|
|
e21a8b6717 | ||
|
|
7047869367 | ||
|
|
53d4fd1d0b | ||
|
|
ccf3fe40ec | ||
|
|
3a63bcab73 | ||
|
|
30dade1fba | ||
|
|
97d6a34b8c | ||
|
|
431af5f530 | ||
|
|
3e3bb594ea | ||
|
|
fb001caee4 | ||
|
|
b73f20cadf | ||
|
|
3fb62874f0 | ||
|
|
c66804bcee | ||
|
|
e1af69f6ae | ||
|
|
27bea2abb9 | ||
|
|
1b18cd7fe6 | ||
|
|
78c400e948 | ||
|
|
75d7e3e1a0 | ||
|
|
5948679a4a | ||
|
|
774f21bb7f | ||
|
|
20367eecc9 | ||
|
|
cda9dd57dd | ||
|
|
41b65bd9a2 | ||
|
|
17a83129b9 | ||
|
|
0e60184e95 | ||
|
|
1ea0de8bca | ||
|
|
17ccfa9ada | ||
|
|
c1daabef08 | ||
|
|
dc39d2c567 | ||
|
|
321e7c93ec | ||
|
|
8456b3ec0c | ||
|
|
e73b16846e |
@@ -2961,6 +2961,42 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Singrity",
|
||||
"name": "Bogdan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58479551?v=4",
|
||||
"profile": "http://@singrity",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mmanjos",
|
||||
"name": "mmanjos",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3483684?v=4",
|
||||
"profile": "https://github.com/mmanjos",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Azooz2014",
|
||||
"name": "Abdelaziz Faki",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7429229?v=4",
|
||||
"profile": "https://azooz2014.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bilias",
|
||||
"name": "bilias",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/47315739?v=4",
|
||||
"profile": "https://github.com/bilias",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ LOG_CHANNEL=stderr
|
||||
LOG_MAX_DAYS=10
|
||||
APP_LOCKED=false
|
||||
APP_CIPHER=AES-256-CBC
|
||||
APP_FORCE_TLS=false
|
||||
GOOGLE_MAPS_API=
|
||||
LDAP_MEM_LIM=500M
|
||||
LDAP_TIME_LIM=600
|
||||
|
||||
11
.env.example
11
.env.example
@@ -127,6 +127,17 @@ PUBLIC_AWS_BUCKET=null
|
||||
PUBLIC_AWS_URL=null
|
||||
PUBLIC_AWS_BUCKET_ROOT=null
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: Digital Ocean Spaces File Settings
|
||||
# --------------------------------------------
|
||||
DIGITALOCEAN_SPACES_KEY=null
|
||||
DIGITALOCEAN_SPACES_SECRET=null
|
||||
DIGITALOCEAN_SPACES_ENDPOINT=https://region.digitaloceanspaces.com
|
||||
DIGITALOCEAN_SPACES_REGION=null
|
||||
DIGITALOCEAN_SPACES_BUCKET=null
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: PRIVATE S3 Settings
|
||||
# --------------------------------------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[](https://crowdin.com/project/snipe-it) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors) [](https://discord.gg/yZFtShAcKk) [](https://huntr.dev)
|
||||
[](#contributors) [](https://discord.gg/yZFtShAcKk) [](https://huntr.dev)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
@@ -145,7 +145,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<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") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -18,7 +18,7 @@ class LdapSync extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--filter=} {--summary} {--json_summary}';
|
||||
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=*} {--base_dn=} {--filter=} {--summary} {--json_summary}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -83,7 +83,16 @@ class LdapSync extends Command
|
||||
$summary = [];
|
||||
|
||||
try {
|
||||
if ($this->option('base_dn') != '') {
|
||||
if ( $this->option('location_id') != '') {
|
||||
|
||||
foreach($this->option('location_id') as $location_id){
|
||||
$location_ou= Location::where('id', '=', $location_id)->value('ldap_ou');
|
||||
$search_base = $location_ou;
|
||||
Log::debug('Importing users from specified location OU: \"'.$search_base.'\".');
|
||||
}
|
||||
}
|
||||
|
||||
else if ($this->option('base_dn') != '') {
|
||||
$search_base = $this->option('base_dn');
|
||||
Log::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
} else {
|
||||
@@ -106,17 +115,21 @@ class LdapSync extends Command
|
||||
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose
|
||||
if ($this->option('location') != '') {
|
||||
if ($location = Location::where('name', '=', $this->option('location'))->first()) {
|
||||
Log::debug('Location name ' . $this->option('location') . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
}
|
||||
|
||||
if ($this->option('location') != '') {
|
||||
$location = Location::where('name', '=', $this->option('location'))->first();
|
||||
Log::debug('Location name '.$this->option('location').' passed');
|
||||
Log::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
} elseif ($this->option('location_id') != '') {
|
||||
$location = Location::where('id', '=', $this->option('location_id'))->first();
|
||||
Log::debug('Location ID '.$this->option('location_id').' passed');
|
||||
Log::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
} elseif ($this->option('location_id') != '') {
|
||||
foreach($this->option('location_id') as $location_id) {
|
||||
if ($location = Location::where('id', '=', $location_id)->first()) {
|
||||
Log::debug('Location ID ' . $location_id . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($location)) {
|
||||
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
|
||||
class RotateAppKey extends Command
|
||||
@@ -16,14 +17,17 @@ class RotateAppKey extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:rotate-key';
|
||||
protected $signature = 'snipeit:rotate-key
|
||||
{previous_key? : The previous key to rotate from}
|
||||
{--emergency : Emergency mode - rotate from .env APP_KEY to newly-generated one, modifying .env}
|
||||
{--force : Skip interactive confirmation}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
protected $description = 'Rotates APP_KEY to a new value, optionally taking the previous key as an argument';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@@ -42,26 +46,42 @@ class RotateAppKey extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->confirm("\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ")) {
|
||||
//make sure they specify only exactly one of --emergency, or a filename. Not neither, and not both.
|
||||
if ( (!$this->option('emergency') && !$this->argument('previous_key')) || ( $this->option('emergency') && $this->argument('previous_key'))) {
|
||||
$this->error("Specify only one of --emergency, or an app key value, in order to rotate keys");
|
||||
return 1;
|
||||
}
|
||||
if ( $this->option('emergency') ) {
|
||||
$msg = "\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ";
|
||||
} else {
|
||||
$msg = "\n****************************************************\nTHIS WILL DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND RE-ENCRYPT THEM WITH YOUR\nAPP_KEY.\n\nThere is NO undo. \n\nMake SURE you have a database backup BEFORE running this command. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup? ";
|
||||
}
|
||||
if ($this->option('force') || $this->confirm($msg)) {
|
||||
|
||||
// Get the existing app_key and ciphers
|
||||
// We put them in a variable since we clear the cache partway through here.
|
||||
$old_app_key = config('app.key');
|
||||
$cipher = config('app.cipher');
|
||||
if ($this->option('emergency')) {
|
||||
$old_app_key = config('app.key');
|
||||
$cipher = config('app.cipher');
|
||||
|
||||
// Generate a new one
|
||||
Artisan::call('key:generate', ['--show' => true]);
|
||||
$new_app_key = Artisan::output();
|
||||
// Generate a new one
|
||||
Artisan::call('key:generate', ['--show' => true]);
|
||||
$new_app_key = trim(Artisan::output());
|
||||
|
||||
// Clear the config cache
|
||||
Artisan::call('config:clear');
|
||||
// Clear the config cache
|
||||
Artisan::call('config:clear');
|
||||
|
||||
$this->warn('Your app cipher is: '.$cipher);
|
||||
$this->warn('Your old APP_KEY is: '.$old_app_key);
|
||||
$this->warn('Your new APP_KEY is: '.$new_app_key);
|
||||
// Write the new app key to the .env file
|
||||
$this->writeNewEnvironmentFileWith($new_app_key);
|
||||
} elseif ($this->argument('previous_key')) {
|
||||
$old_app_key = $this->argument('previous_key');
|
||||
$cipher = config('app.cipher'); // just a guess?
|
||||
$new_app_key = config('app.key');
|
||||
}
|
||||
|
||||
// Write the new app key to the .env file
|
||||
$this->writeNewEnvironmentFileWith($new_app_key);
|
||||
$this->warn('Your app cipher is: ' . $cipher);
|
||||
$this->warn('Your old APP_KEY is: ' . $old_app_key);
|
||||
$this->warn('Your new APP_KEY is: ' . $new_app_key);
|
||||
|
||||
// Manually create an old encrypter instance using the old app key
|
||||
// and also create a new encrypter instance so we can re-crypt the field
|
||||
@@ -75,8 +95,16 @@ class RotateAppKey extends Command
|
||||
$assets = Asset::whereNotNull($field->db_column)->get();
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
|
||||
$this->line('DECRYPTED: '.$field->db_column);
|
||||
try {
|
||||
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
|
||||
$this->line('DECRYPTED: ' . $field->db_column);
|
||||
} catch (DecryptException $e) {
|
||||
$this->line('Could not decrypt '. $field->db_column.' using "old key" - skipping...');
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
$this->error("Error decrypting ".$field->db_column.", reason: ".$e->getMessage().". Aborting key rotation");
|
||||
throw $e;
|
||||
}
|
||||
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
|
||||
$this->line('ENCRYPTED: '.$field->db_column);
|
||||
$asset->save();
|
||||
@@ -86,10 +114,14 @@ class RotateAppKey extends Command
|
||||
// Handle the LDAP password if one is provided
|
||||
$setting = Setting::first();
|
||||
if ($setting->ldap_pword != '') {
|
||||
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
|
||||
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
|
||||
$setting->save();
|
||||
$this->warn('LDAP password has been re-encrypted.');
|
||||
try {
|
||||
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
|
||||
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
|
||||
$setting->save();
|
||||
$this->warn('LDAP password has been re-encrypted.');
|
||||
} catch(DecryptException $e) {
|
||||
$this->warn("Unable to decrypt old LDAP password; skipping");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->info('This operation has been canceled. No changes have been made.');
|
||||
@@ -106,7 +138,7 @@ class RotateAppKey extends Command
|
||||
{
|
||||
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
|
||||
$this->keyReplacementPattern(),
|
||||
'APP_KEY='.$key,
|
||||
'APP_KEY="'.$key.'"',
|
||||
file_get_contents($this->laravel->environmentFilePath())
|
||||
));
|
||||
}
|
||||
@@ -118,7 +150,7 @@ class RotateAppKey extends Command
|
||||
*/
|
||||
protected function keyReplacementPattern()
|
||||
{
|
||||
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
|
||||
$escaped = '="?'.preg_quote($this->laravel['config']['app.key'], '/').'"?';
|
||||
|
||||
return "/^APP_KEY{$escaped}/m";
|
||||
}
|
||||
|
||||
@@ -150,6 +150,11 @@ class Handler extends ExceptionHandler
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
|
||||
protected function invalidJson($request, ValidationException $exception)
|
||||
{
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $exception->errors()), 200);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
|
||||
@@ -73,10 +73,14 @@ class Helper
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.3]
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
public static function defaultChartColors($index = 0)
|
||||
public static function defaultChartColors(int $index = 0)
|
||||
{
|
||||
if ($index < 0) {
|
||||
$index = 0;
|
||||
}
|
||||
|
||||
$colors = [
|
||||
'#008941',
|
||||
'#FF4A46',
|
||||
@@ -349,7 +353,19 @@ class Helper
|
||||
$total_colors = count($colors);
|
||||
|
||||
if ($index >= $total_colors) {
|
||||
$index = $index - $total_colors;
|
||||
|
||||
\Log::error('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
//patch fix for array key overflow (color count starts at 1, array starts at 0)
|
||||
$index = $index - $total_colors - 1;
|
||||
|
||||
//constraints to keep result in 0-265 range. This should never be needed, but if something happens
|
||||
//to create this many status labels and it DOES happen, this will keep it from failing at least.
|
||||
if($index < 0) {
|
||||
$index = 0;
|
||||
}
|
||||
elseif($index >($total_colors - 1)) {
|
||||
$index = $total_colors - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $colors[$index];
|
||||
|
||||
@@ -146,9 +146,8 @@ class AccessoriesFilesController extends Controller
|
||||
$this->authorize('view', $accessory);
|
||||
$this->authorize('accessories.files', $accessory);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/users/message.log_record_not_found'));
|
||||
}
|
||||
|
||||
$file = 'private_uploads/accessories/'.$log->filename;
|
||||
|
||||
@@ -18,31 +18,36 @@ class AccessoryCheckoutController extends Controller
|
||||
* Return the form to checkout an Accessory to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $accessoryId
|
||||
* @param int $id
|
||||
* @return View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create($accessoryId)
|
||||
public function create($id)
|
||||
{
|
||||
// Check if the accessory exists
|
||||
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($accessory->numRemaining() <= 0){
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable'));
|
||||
}
|
||||
|
||||
if ($accessory->category) {
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($id)) {
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
return view('accessories/checkout', compact('accessory'));
|
||||
if ($accessory->category) {
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($accessory->numRemaining() <= 0){
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable'));
|
||||
}
|
||||
|
||||
// Return the checkout view
|
||||
return view('accessories/checkout', compact('accessory'));
|
||||
}
|
||||
|
||||
// Invalid category
|
||||
return redirect()->route('accessories.edit', ['accessory' => $accessory->id])
|
||||
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.accessory')]));
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'The category type for this accessory is not valid. Edit the accessory and select a valid accessory category.');
|
||||
// Not found
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ class AssetModelsController extends Controller
|
||||
'requestable',
|
||||
'assets_count',
|
||||
'category',
|
||||
'fieldset',
|
||||
];
|
||||
|
||||
$assetmodels = AssetModel::select([
|
||||
@@ -94,6 +95,9 @@ class AssetModelsController extends Controller
|
||||
case 'category':
|
||||
$assetmodels->OrderCategory($order);
|
||||
break;
|
||||
case 'fieldset':
|
||||
$assetmodels->OrderFieldset($order);
|
||||
break;
|
||||
default:
|
||||
$assetmodels->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -33,6 +33,7 @@ use TCPDF;
|
||||
use Validator;
|
||||
use Route;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls all actions related to assets for
|
||||
* the Snipe-IT Asset Management application.
|
||||
@@ -48,7 +49,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request, $audit = null)
|
||||
{
|
||||
@@ -295,7 +296,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('order_number')) {
|
||||
$assets->where('assets.order_number', '=', $request->get('order_number'));
|
||||
$assets->where('assets.order_number', '=', strval($request->get('order_number')));
|
||||
}
|
||||
|
||||
// This is kinda gross, but we need to do this because the Bootstrap Tables
|
||||
@@ -346,7 +347,7 @@ class AssetsController extends Controller
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$total = $assets->count();
|
||||
@@ -443,7 +444,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
@@ -474,7 +475,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function selectlist(Request $request)
|
||||
{
|
||||
@@ -530,12 +531,12 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
|
||||
$asset = new Asset();
|
||||
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
|
||||
|
||||
@@ -545,8 +546,7 @@ class AssetsController extends Controller
|
||||
$asset->model_id = $request->get('model_id');
|
||||
$asset->order_number = $request->get('order_number');
|
||||
$asset->notes = $request->get('notes');
|
||||
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset()); //yup, problem :/
|
||||
// NO IT IS NOT!!! This is never firing; we SHOW the asset_tag you're going to get, so it *will* be filled in!
|
||||
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
|
||||
$asset->user_id = Auth::id();
|
||||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
@@ -639,7 +639,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $id)
|
||||
{
|
||||
@@ -666,10 +666,11 @@ class AssetsController extends Controller
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = AssetModel::find($asset->model_id);
|
||||
|
||||
// Update custom fields
|
||||
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
@@ -720,7 +721,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
@@ -749,38 +750,28 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v5.1.18]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function restore(Request $request, $assetId = null)
|
||||
{
|
||||
// Get asset information
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
$this->authorize('delete', $asset);
|
||||
|
||||
if (isset($asset->id)) {
|
||||
if ($asset = Asset::withTrashed()->find($assetId)) {
|
||||
$this->authorize('delete', $asset);
|
||||
|
||||
if ($asset->deleted_at=='') {
|
||||
$message = 'Asset was not deleted. No data was changed.';
|
||||
|
||||
} else {
|
||||
|
||||
$message = trans('admin/hardware/message.restore.success');
|
||||
// Restore the asset
|
||||
Asset::withTrashed()->where('id', $assetId)->restore();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Asset::class;
|
||||
$logaction->item_id = $asset->id;
|
||||
$logaction->created_at = date("Y-m-d H:i:s");
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restored');
|
||||
if ($asset->deleted_at == '') {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.asset')])), 200);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset, $request), $message));
|
||||
|
||||
if ($asset->restore()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/hardware/message.restore.success')), 200);
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.asset'), 'error' => $asset->getErrors()->first()])), 200);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -789,7 +780,7 @@ class AssetsController extends Controller
|
||||
* @author [N. Butler]
|
||||
* @param string $tag
|
||||
* @since [v6.0.5]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function checkoutByTag(AssetCheckoutRequest $request, $tag)
|
||||
{
|
||||
@@ -805,7 +796,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id)
|
||||
{
|
||||
@@ -889,7 +880,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function checkin(Request $request, $asset_id)
|
||||
{
|
||||
@@ -945,7 +936,7 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @author [A. Janes] [<ajanes@adagiohealth.org>]
|
||||
* @since [v6.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function checkinByTag(Request $request, $tag = null)
|
||||
{
|
||||
@@ -971,7 +962,7 @@ class AssetsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function audit(Request $request)
|
||||
|
||||
@@ -1032,24 +1023,54 @@ class AssetsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function requestable(Request $request)
|
||||
{
|
||||
$this->authorize('viewRequestable', Asset::class);
|
||||
|
||||
$allowed_columns = [
|
||||
'name',
|
||||
'asset_tag',
|
||||
'serial',
|
||||
'model_number',
|
||||
'image',
|
||||
'purchase_cost',
|
||||
'expected_checkin',
|
||||
];
|
||||
|
||||
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
|
||||
|
||||
foreach ($all_custom_fields as $field) {
|
||||
$allowed_columns[] = $field->db_column_name();
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier')
|
||||
->with('location', 'assetstatus', 'assetlog', 'company','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests')
|
||||
->requestableAssets();
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
|
||||
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())) {
|
||||
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
}
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort_override = str_replace('custom_fields.', '', $request->input('sort'));
|
||||
|
||||
// This handles all the pivot sorting (versus the assets.* fields
|
||||
// in the allowed_columns array)
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
@@ -1057,17 +1078,19 @@ class AssetsController extends Controller
|
||||
case 'model_number':
|
||||
$assets->OrderModelNumber($order);
|
||||
break;
|
||||
case 'category':
|
||||
$assets->OrderCategory($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$assets->OrderManufacturer($order);
|
||||
case 'location':
|
||||
$assets->OrderLocation($order);
|
||||
break;
|
||||
default:
|
||||
$assets->orderBy('assets.created_at', $order);
|
||||
$assets->orderBy($column_sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class CategoriesController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $categories->count()) ? $categories->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $categories->count()) ? $categories->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -56,7 +56,7 @@ 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() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $companies->count()) ? $companies->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ class ComponentsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $components->count()) ? $components->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $components->count()) ? $components->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -263,7 +263,7 @@ class ComponentsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($component->numRemaining() <= $request->get('assigned_qty')) {
|
||||
if ($component->numRemaining() < $request->get('assigned_qty')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class ConsumablesController extends Controller
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $consumables->count()) ? $consumables->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$allowed_columns = ['id', 'name', 'order_number', 'min_amt', 'purchase_date', 'purchase_cost', 'company', 'category', 'model_number', 'item_no', 'manufacturer', 'location', 'qty', 'image'];
|
||||
@@ -263,9 +263,14 @@ class ConsumablesController extends Controller
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($consumable->numRemaining() <= 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable')));
|
||||
\Log::debug('No enough remaining');
|
||||
}
|
||||
|
||||
// Make sure there is a valid category
|
||||
if (!$consumable->category){
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')])));
|
||||
}
|
||||
|
||||
|
||||
// Check if the user exists - @TODO: this should probably be handled via validation, not here??
|
||||
if (!$user = User::find($request->input('assigned_to'))) {
|
||||
// Return error message
|
||||
|
||||
@@ -61,7 +61,7 @@ class DepartmentsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $departments->count()) ? $departments->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $departments->count()) ? $departments->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -29,7 +29,7 @@ class DepreciationsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $depreciations->count()) ? $depreciations->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -36,7 +36,7 @@ class GroupsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $groups->count()) ? $groups->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $groups->count()) ? $groups->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -63,7 +63,7 @@ class GroupsController extends Controller
|
||||
$group = new Group;
|
||||
|
||||
$group->name = $request->input('name');
|
||||
$group->permissions = $request->input('permissions'); // Todo - some JSON validation stuff here
|
||||
$group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here
|
||||
|
||||
if ($group->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $group, trans('admin/groups/message.create.success')));
|
||||
|
||||
@@ -41,7 +41,7 @@ class LicenseSeatsController extends Controller
|
||||
$total = $seats->count();
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $seats->count()) ? $seats->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $seats->count()) ? $seats->count() : app('api_offset_value');
|
||||
|
||||
if ($offset >= $total ){
|
||||
$offset = 0;
|
||||
|
||||
@@ -95,7 +95,7 @@ class LicensesController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -81,7 +81,7 @@ class LocationsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -6,9 +6,11 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ManufacturersTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ManufacturersController extends Controller
|
||||
@@ -62,7 +64,7 @@ class ManufacturersController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $manufacturers->count()) ? $manufacturers->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
@@ -159,6 +161,44 @@ class ManufacturersController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a given Manufacturer (mark as un-deleted)
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v6.3.4]
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function restore($id)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
|
||||
if ($manufacturer = Manufacturer::withTrashed()->find($id)) {
|
||||
|
||||
if ($manufacturer->deleted_at == '') {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.manufacturer')])), 200);
|
||||
}
|
||||
|
||||
if ($manufacturer->restore()) {
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Manufacturer::class;
|
||||
$logaction->item_id = $manufacturer->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/manufacturers/message.restore.success')), 200);
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring an item with the same unique attributes as a non-deleted one
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.manufacturer'), 'error' => $manufacturer->getErrors()->first()])), 200);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.does_not_exist')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a paginated collection for the select2 menus
|
||||
*
|
||||
|
||||
@@ -30,7 +30,7 @@ class PredefinedKitsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $kits->count()) ? $kits->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $kits->count()) ? $kits->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'desc' ? 'desc' : 'asc';
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Http\Request;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Models\CustomField;
|
||||
use DB;
|
||||
|
||||
class ProfileController extends Controller
|
||||
@@ -48,14 +49,23 @@ class ProfileController extends Controller
|
||||
{
|
||||
$checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
|
||||
|
||||
$results = [];
|
||||
$results = array();
|
||||
$show_field = array();
|
||||
$showable_fields = array();
|
||||
$results['total'] = $checkoutRequests->count();
|
||||
|
||||
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) {
|
||||
$showable_fields[] = $field->db_column_name();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($checkoutRequests as $checkoutRequest) {
|
||||
|
||||
// Make sure the asset and request still exist
|
||||
if ($checkoutRequest && $checkoutRequest->itemRequested()) {
|
||||
$results['rows'][] = [
|
||||
$assets = [
|
||||
'image' => e($checkoutRequest->itemRequested()->present()->getImageUrl()),
|
||||
'name' => e($checkoutRequest->itemRequested()->present()->name()),
|
||||
'type' => e($checkoutRequest->itemType()),
|
||||
@@ -64,7 +74,16 @@ class ProfileController extends Controller
|
||||
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
|
||||
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
foreach ($showable_fields as $showable_field_name) {
|
||||
$show_field['custom_fields.'.$showable_field_name] = $checkoutRequest->itemRequested()->{$showable_field_name};
|
||||
}
|
||||
|
||||
// Merge the plain asset data and the custom fields data
|
||||
$results['rows'][] = array_merge($assets, $show_field);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
@@ -56,7 +56,7 @@ class ReportsController extends Controller
|
||||
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $actionlogs->count()) ? $actionlogs->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
@@ -52,7 +52,7 @@ class StatuslabelsController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $statuslabels->count()) ? $statuslabels->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -94,7 +94,7 @@ class SuppliersController extends Controller
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $suppliers->count()) ? $suppliers->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\UsersTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\License;
|
||||
@@ -192,7 +193,7 @@ class UsersController extends Controller
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : abs($request->input('offset'));
|
||||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
|
||||
@@ -688,17 +689,31 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function restore($userId = null)
|
||||
{
|
||||
// Get asset information
|
||||
$user = User::withTrashed()->find($userId);
|
||||
$this->authorize('delete', $user);
|
||||
if (isset($user->id)) {
|
||||
// Restore the user
|
||||
User::withTrashed()->where('id', $userId)->restore();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored')));
|
||||
if ($user = User::withTrashed()->find($userId)) {
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
if ($user->deleted_at == '') {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.user')])), 200);
|
||||
}
|
||||
|
||||
if ($user->restore()) {
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = User::class;
|
||||
$logaction->item_id = $user->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/users/message.restore.success')), 200);
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring a user with the same username as an existing user
|
||||
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()])), 200);
|
||||
}
|
||||
|
||||
$id = $userId;
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found', compact('id'))), 200);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -173,8 +177,20 @@ class AssetModelsController extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if ($model->save()) {
|
||||
if ($model->wasChanged('eol')) {
|
||||
if ($model->eol > 0) {
|
||||
$newEol = $model->eol;
|
||||
$model->assets()->whereNotNull('purchase_date')->where('eol_explicit', false)
|
||||
->update(['asset_eol_date' => DB::raw('DATE_ADD(purchase_date, INTERVAL ' . $newEol . ' MONTH)')]);
|
||||
} elseif ($model->eol == 0) {
|
||||
$model->assets()->whereNotNull('purchase_date')->where('eol_explicit', false)
|
||||
->update(['asset_eol_date' => DB::raw('null')]);
|
||||
}
|
||||
}
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.update.success'));
|
||||
}
|
||||
|
||||
@@ -196,7 +212,7 @@ class AssetModelsController extends Controller
|
||||
$this->authorize('delete', AssetModel::class);
|
||||
// Check if the model exists
|
||||
if (is_null($model = AssetModel::find($modelId))) {
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found'));
|
||||
return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
|
||||
}
|
||||
|
||||
if ($model->assets()->count() > 0) {
|
||||
@@ -224,22 +240,42 @@ class AssetModelsController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param int $modelId
|
||||
* @param int $id
|
||||
* @return Redirect
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function getRestore($modelId = null)
|
||||
public function getRestore($id)
|
||||
{
|
||||
$this->authorize('create', AssetModel::class);
|
||||
// Get user information
|
||||
$model = AssetModel::withTrashed()->find($modelId);
|
||||
|
||||
if (isset($model->id)) {
|
||||
$model->restore();
|
||||
if ($model = AssetModel::withTrashed()->find($id)) {
|
||||
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.restore.success'));
|
||||
if ($model->deleted_at == '') {
|
||||
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.asset_model')]));
|
||||
}
|
||||
|
||||
if ($model->restore()) {
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = User::class;
|
||||
$logaction->item_id = $model->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
|
||||
// Redirect them to the deleted page if there are more, otherwise the section index
|
||||
$deleted_models = AssetModel::onlyTrashed()->count();
|
||||
if ($deleted_models > 0) {
|
||||
return redirect()->back()->with('success', trans('admin/models/message.restore.success'));
|
||||
}
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.restore.success'));
|
||||
}
|
||||
|
||||
// Check validation
|
||||
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.asset_model'), 'error' => $model->getErrors()->first()]));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,8 @@ class AssetCheckoutController extends Controller
|
||||
|
||||
$settings = \App\Models\Setting::getSettings();
|
||||
|
||||
if ($settings->full_multiple_companies_support){
|
||||
// We have to check whether $target->company_id is null here since locations don't have a company yet
|
||||
if (($settings->full_multiple_companies_support) && ((!is_null($target->company_id)) && (!is_null($asset->company_id)))) {
|
||||
if ($target->company_id != $asset->company_id){
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('general.error_user_company'));
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class AssetFilesController extends Controller
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CheckoutRequest;
|
||||
@@ -14,26 +16,18 @@ use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\View\Label;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
use Input;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Paginator;
|
||||
use Redirect;
|
||||
use Response;
|
||||
use Slack;
|
||||
use Str;
|
||||
use TCPDF;
|
||||
use View;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
|
||||
/**
|
||||
* This class controls all actions related to assets for
|
||||
@@ -144,7 +138,7 @@ class AssetsController extends Controller
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = request('purchase_cost');
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->asset_eol_date = request('asset_eol_date', $asset->present()->eol_date());
|
||||
$asset->asset_eol_date = request('asset_eol_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', null);
|
||||
$asset->requestable = request('requestable', 0);
|
||||
@@ -173,9 +167,9 @@ class AssetsController extends Controller
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column));
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -211,12 +205,9 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
// Redirect to the asset listing page
|
||||
$minutes = 518400;
|
||||
// dd( $_POST['options']);
|
||||
// Cookie::queue(Cookie::make('optional_info', json_decode($_POST['options']), $minutes));
|
||||
\Log::debug(e($asset->asset_tag));
|
||||
return redirect()->route('hardware.index')
|
||||
->with('success', trans('admin/hardware/message.create.success'));
|
||||
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => e($asset->asset_tag)]));
|
||||
|
||||
|
||||
}
|
||||
@@ -298,10 +289,10 @@ class AssetsController extends Controller
|
||||
/**
|
||||
* Validate and process asset edit form.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
* @return \Illuminate\Http\RedirectResponse|Redirect
|
||||
*@since [v1.0]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $assetId = null)
|
||||
{
|
||||
@@ -315,9 +306,27 @@ class AssetsController extends Controller
|
||||
$asset->status_id = $request->input('status_id', null);
|
||||
$asset->warranty_months = $request->input('warranty_months', null);
|
||||
$asset->purchase_cost = $request->input('purchase_cost', null);
|
||||
$asset->asset_eol_date = request('asset_eol_date', null);
|
||||
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
if ($request->filled('purchase_date') && !$request->filled('asset_eol_date') && ($asset->model->eol > 0)) {
|
||||
$asset->purchase_date = $request->input('purchase_date', null);
|
||||
$asset->asset_eol_date = Carbon::parse($request->input('purchase_date'))->addMonths($asset->model->eol)->format('Y-m-d');
|
||||
$asset->eol_explicit = false;
|
||||
} elseif ($request->filled('asset_eol_date')) {
|
||||
$asset->asset_eol_date = $request->input('asset_eol_date', null);
|
||||
$months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
if($asset->model->eol) {
|
||||
if($months != $asset->model->eol > 0) {
|
||||
$asset->eol_explicit = true;
|
||||
} else {
|
||||
$asset->eol_explicit = false;
|
||||
}
|
||||
} else {
|
||||
$asset->eol_explicit = true;
|
||||
}
|
||||
} elseif (!$request->filled('asset_eol_date') && (($asset->model->eol) == 0)) {
|
||||
$asset->asset_eol_date = null;
|
||||
$asset->eol_explicit = false;
|
||||
}
|
||||
$asset->supplier_id = $request->input('supplier_id', null);
|
||||
$asset->expected_checkin = $request->input('expected_checkin', null);
|
||||
|
||||
@@ -342,7 +351,7 @@ class AssetsController extends Controller
|
||||
unlink(public_path().'/uploads/assets/'.$asset->image);
|
||||
$asset->image = '';
|
||||
} catch (\Exception $e) {
|
||||
\Log::info($e);
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,9 +379,9 @@ class AssetsController extends Controller
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('admin')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column));
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -420,7 +429,7 @@ class AssetsController extends Controller
|
||||
try {
|
||||
Storage::disk('public')->delete('assets'.'/'.$asset->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
Log::debug($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,7 +544,7 @@ class AssetsController extends Controller
|
||||
|
||||
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('The barcode format is invalid.');
|
||||
Log::debug('The barcode format is invalid.');
|
||||
|
||||
return response(file_get_contents(public_path('uploads/barcodes/invalid_barcode.gif')))->header('Content-type', 'image/gif');
|
||||
}
|
||||
@@ -787,21 +796,24 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function getRestore($assetId = null)
|
||||
{
|
||||
// Get asset information
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
$this->authorize('delete', $asset);
|
||||
if (isset($asset->id)) {
|
||||
// Restore the asset
|
||||
Asset::withTrashed()->where('id', $assetId)->restore();
|
||||
if ($asset = Asset::withTrashed()->find($assetId)) {
|
||||
$this->authorize('delete', $asset);
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Asset::class;
|
||||
$logaction->item_id = $asset->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restored');
|
||||
if ($asset->deleted_at == '') {
|
||||
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.asset')]));
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
|
||||
if ($asset->restore()) {
|
||||
// Redirect them to the deleted page if there are more, otherwise the section index
|
||||
$deleted_assets = Asset::onlyTrashed()->count();
|
||||
if ($deleted_assets > 0) {
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.restore.success'));
|
||||
}
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
|
||||
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.asset'), 'error' => $asset->getErrors()->first()]));
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
@@ -856,7 +868,7 @@ class AssetsController extends Controller
|
||||
'next_audit_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
||||
@@ -873,7 +885,7 @@ class AssetsController extends Controller
|
||||
// Check to see if they checked the box to update the physical location,
|
||||
// not just note it in the audit notes
|
||||
if ($request->input('update_location') == '1') {
|
||||
\Log::debug('update location in audit');
|
||||
Log::debug('update location in audit');
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Setting;
|
||||
use App\View\Label;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -23,6 +25,13 @@ class BulkAssetsController extends Controller
|
||||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* This method is super weird because it's kinda of like a controller within a controller.
|
||||
* It's main function is to determine what the bulk action in, and then return a view with
|
||||
* the information that view needs, be it bulk delete, bulk edit, restore, etc.
|
||||
*
|
||||
* This is something that made sense at the time, but sort of doesn't make sense now. A JS front-end to determine form
|
||||
* action would make a lot more sense here and make things a lot more clear.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
@@ -32,7 +41,10 @@ class BulkAssetsController extends Controller
|
||||
public function edit(Request $request)
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
|
||||
/**
|
||||
* No asset IDs were passed
|
||||
*/
|
||||
if (! $request->filled('ids')) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
@@ -41,59 +53,52 @@ class BulkAssetsController extends Controller
|
||||
$bulk_back_url = request()->headers->get('referer');
|
||||
session(['bulk_back_url' => $bulk_back_url]);
|
||||
|
||||
$asset_ids = array_values(array_unique($request->input('ids')));
|
||||
|
||||
//custom fields logic
|
||||
$asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) {
|
||||
return $query->where('fieldset_id', '!=', null);
|
||||
})->get();
|
||||
|
||||
$models = $asset_custom_field->unique('model_id');
|
||||
$asset_ids = $request->input('ids');
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')->find($asset_ids);
|
||||
|
||||
$models = $assets->unique('model_id');
|
||||
$modelNames = [];
|
||||
foreach($models as $model) {
|
||||
$modelNames[] = $model->model->name;
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('bulk_actions')) {
|
||||
|
||||
|
||||
switch ($request->input('bulk_actions')) {
|
||||
case 'labels':
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets_found = Asset::find($asset_ids);
|
||||
|
||||
if ($assets_found->isEmpty()){
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
return (new Label)
|
||||
->with('assets', $assets_found)
|
||||
->with('assets', $assets)
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('bulkedit', true)
|
||||
->with('count', 0);
|
||||
|
||||
case 'delete':
|
||||
$this->authorize('delete', Asset::class);
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
$assets->each(function ($assets) {
|
||||
$this->authorize('delete', $assets);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
|
||||
case 'restore':
|
||||
$this->authorize('update', Asset::class);
|
||||
$assets = Asset::withTrashed()->find($asset_ids);
|
||||
$assets = Asset::withTrashed()->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-restore')->with('assets', $assets);
|
||||
|
||||
case 'edit':
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
return view('hardware/bulk')
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('models', $models->pluck(['model']))
|
||||
->with('models', $models->pluck(['model']))
|
||||
->with('modelNames', $modelNames);
|
||||
}
|
||||
}
|
||||
@@ -112,30 +117,36 @@ class BulkAssetsController extends Controller
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$error_bag = [];
|
||||
$has_errors = 0;
|
||||
$error_array = array();
|
||||
|
||||
// Get the back url from the session and then destroy the session
|
||||
$bulk_back_url = route('hardware.index');
|
||||
|
||||
if ($request->session()->has('bulk_back_url')) {
|
||||
$bulk_back_url = $request->session()->pull('bulk_back_url');
|
||||
}
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
if(Session::exists('ids')) {
|
||||
$assets = Session::get('ids');
|
||||
} elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) {
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
||||
$assets = array_keys($request->input('ids'));
|
||||
|
||||
if ($request->anyFilled($custom_field_columns)) {
|
||||
$custom_fields_present = true;
|
||||
} else {
|
||||
$custom_fields_present = false;
|
||||
}
|
||||
|
||||
|
||||
$assets = Asset::whereIn('id', array_keys($request->input('ids')))->get();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If ANY of these are filled, prepare to update the values on the assets.
|
||||
*
|
||||
* Additional checks will be needed for some of them to make sure the values
|
||||
* make sense (for example, changing the status ID to something incompatible with
|
||||
* its checkout status.
|
||||
*/
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
|| ($request->filled('expected_checkin'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
@@ -154,23 +165,32 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->anyFilled($custom_field_columns))
|
||||
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
// Let's loop through those assets and build an update array
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$this->update_array = [];
|
||||
|
||||
/**
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some extra
|
||||
* validation and checks on those two.
|
||||
*
|
||||
* It's tempting to make these match the request check above, but some of these values require
|
||||
* extra work to make sure the data makes sense.
|
||||
*/
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('expected_checkin')
|
||||
->conditionallyAddItem('model_id')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
->conditionallyAddItem('status_id')
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months')
|
||||
->conditionallyAddItem('next_audit_date');
|
||||
foreach ($custom_field_columns as $key => $custom_field_column) {
|
||||
$this->conditionallyAddItem($custom_field_column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blank out fields that were requested to be blanked out via checkbox
|
||||
*/
|
||||
if ($request->input('null_purchase_date')=='1') {
|
||||
$this->update_array['purchase_date'] = null;
|
||||
}
|
||||
@@ -194,69 +214,150 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the model ID - we need to do some extra checks here to make sure
|
||||
* the custom field values work for the custom fieldset rules around this asset. Uniqueness
|
||||
* and requiredness across the fieldset is particularly important, since those are
|
||||
* fieldset-specific attributes.
|
||||
*/
|
||||
if ($request->filled('model_id')) {
|
||||
$this->update_array['model_id'] = AssetModel::find($request->input('model_id'))->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the status ID - we need to do some extra checks here to
|
||||
* make sure the status label type is one that makes sense for the state of the asset,
|
||||
* for example, we shouldn't be able to make an asset archived if it's currently assigned
|
||||
* to someone/something.
|
||||
*/
|
||||
if ($request->filled('status_id')) {
|
||||
$updated_status = Statuslabel::find($request->input('status_id'));
|
||||
|
||||
// We cannot assign a non-deployable status type if the asset is already assigned.
|
||||
// This could probably be added to a form request.
|
||||
// If the asset isn't assigned, we don't care what the status is.
|
||||
// Otherwise we need to make sure the status type is still a deployable one.
|
||||
if (
|
||||
($asset->assigned_to == '')
|
||||
|| ($updated_status->deployable == '1') && ($asset->assetstatus->deployable == '1')
|
||||
) {
|
||||
$this->update_array['status_id'] = $updated_status->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We're changing the location ID - figure out which location we should apply
|
||||
* this change to:
|
||||
*
|
||||
* 0 - RTD location only
|
||||
* 1 - location ID and RTD location ID
|
||||
* 2 - location ID only
|
||||
*
|
||||
* Note: this is kinda dumb and we should just use human-readable values IMHO. - snipe
|
||||
*/
|
||||
if ($request->filled('rtd_location_id')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '0')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '2')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------------
|
||||
* ANYTHING that happens past this foreach
|
||||
* WILL NOT BE logged in the edit log_meta data
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
$changed = [];
|
||||
$assetCollection = Asset::where('id' ,$assetId)->get();
|
||||
|
||||
foreach ($this->update_array as $key => $value) {
|
||||
if ($this->update_array[$key] != $assetCollection->toArray()[0][$key]) {
|
||||
$changed[$key]['old'] = $assetCollection->toArray()[0][$key];
|
||||
|
||||
if ($this->update_array[$key] != $asset->{$key}) {
|
||||
$changed[$key]['old'] = $asset->{$key};
|
||||
$changed[$key]['new'] = $this->update_array[$key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $assetId;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
$logAction->logaction('update');
|
||||
|
||||
if($custom_fields_present) {
|
||||
$asset = Asset::find($assetId);
|
||||
$assetCustomFields = $asset->model()->first()->fieldset;
|
||||
if($assetCustomFields && $assetCustomFields->fields) {
|
||||
foreach ($assetCustomFields->fields as $field) {
|
||||
if (array_key_exists($field->db_column, $this->update_array)) {
|
||||
$asset->{$field->db_column} = $this->update_array[$field->db_column];
|
||||
$saved = $asset->save();
|
||||
if(!$saved) {
|
||||
$error_bag[] = $asset->getErrors();
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
$array = $this->update_array;
|
||||
array_except($array, $field->db_column);
|
||||
$asset->save($array);
|
||||
}
|
||||
if (!$asset->save()) {
|
||||
$error_bag[] = $asset->getErrors();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Start all the custom fields shenanigans
|
||||
*/
|
||||
|
||||
// Does the model have a fieldset?
|
||||
if ($asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
|
||||
/*
|
||||
* Check if the decrypted existing value is different from one we just submitted
|
||||
* and if not, pull it out of the object since it shouldn't really be updating at all.
|
||||
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
|
||||
* different times will have different values, so it will *look* like it was updated
|
||||
* but it wasn't.
|
||||
*/
|
||||
if ($decrypted_old != $this->update_array[$field->db_column]) {
|
||||
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
|
||||
} else {
|
||||
/*
|
||||
* Remove the encrypted custom field from the update_array, since nothing changed
|
||||
*/
|
||||
unset($this->update_array[$field->db_column]);
|
||||
unset($asset->{$field->db_column});
|
||||
}
|
||||
|
||||
/*
|
||||
* These custom fields aren't encrypted, just carry on as usual
|
||||
*/
|
||||
} else {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
|
||||
|
||||
// Check if this is an array, and if so, flatten it
|
||||
if (is_array($this->update_array[$field->db_column])) {
|
||||
$asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]);
|
||||
} else {
|
||||
$asset->{$field->db_column} = $this->update_array[$field->db_column];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // endforeach
|
||||
}
|
||||
} else {
|
||||
Asset::find($assetId)->update($this->update_array);
|
||||
}
|
||||
}
|
||||
if(!empty($error_bag)) {
|
||||
$errors = [];
|
||||
//find the customfield name from the name of the messagebag items
|
||||
foreach ($error_bag as $key => $bag) {
|
||||
foreach($bag->keys() as $key => $value) {
|
||||
CustomField::where('db_column', $value)->get()->map(function($item) use (&$errors) {
|
||||
$errors[] = $item->name;
|
||||
});
|
||||
|
||||
|
||||
// Check if it passes validation, and then try to save
|
||||
if (!$asset->update($this->update_array)) {
|
||||
|
||||
// Build the error array
|
||||
foreach ($asset->getErrors()->toArray() as $key => $message) {
|
||||
for ($x = 0; $x < count($message); $x++) {
|
||||
$error_array[$key][] = trans('general.asset') . ' ' . $asset->id . ': ' . $message[$x];
|
||||
$has_errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return redirect($bulk_back_url)->with('bulk_errors', array_unique($errors));
|
||||
}
|
||||
|
||||
} // end if saved
|
||||
|
||||
} // end asset foreach
|
||||
|
||||
if ($has_errors > 0) {
|
||||
return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array);
|
||||
}
|
||||
|
||||
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success'));
|
||||
}
|
||||
// no values given, nothing to update
|
||||
|
||||
@@ -56,7 +56,6 @@ class LoginController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware('guest', ['except' => ['logout', 'postTwoFactorAuth', 'getTwoFactorAuth', 'getTwoFactorEnroll']]);
|
||||
Session::put('backUrl', \URL::previous());
|
||||
// $this->ldap = $ldap;
|
||||
$this->saml = $saml;
|
||||
}
|
||||
|
||||
@@ -82,7 +81,6 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
if (Setting::getSettings()->login_common_disabled == '1') {
|
||||
\Log::debug('login_common_disabled is set to 1 - return a 403');
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
@@ -123,7 +121,7 @@ class LoginController extends Controller
|
||||
|
||||
if ($user = Auth::user()) {
|
||||
$user->last_login = \Carbon::now();
|
||||
$user->save();
|
||||
$user->saveQuietly();
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
@@ -199,7 +197,7 @@ class LoginController extends Controller
|
||||
$user->email = $ldap_attr['email'];
|
||||
$user->first_name = $ldap_attr['firstname'];
|
||||
$user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc.
|
||||
$user->save();
|
||||
$user->saveQuietly();
|
||||
} // End if(!user)
|
||||
return $user;
|
||||
}
|
||||
@@ -319,7 +317,7 @@ class LoginController extends Controller
|
||||
if ($user = Auth::user()) {
|
||||
$user->last_login = \Carbon::now();
|
||||
$user->activated = 1;
|
||||
$user->save();
|
||||
$user->saveQuietly();
|
||||
}
|
||||
// Redirect to the users page
|
||||
return redirect()->intended()->with('success', trans('auth/message.signin.success'));
|
||||
@@ -371,7 +369,7 @@ class LoginController extends Controller
|
||||
[-2, -2, -2, -2]
|
||||
);
|
||||
|
||||
$user->save(); // make sure to save *AFTER* displaying the barcode, or else we might save a two_factor_secret that we never actually displayed to the user if the barcode fails
|
||||
$user->saveQuietly(); // make sure to save *AFTER* displaying the barcode, or else we might save a two_factor_secret that we never actually displayed to the user if the barcode fails
|
||||
|
||||
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
|
||||
}
|
||||
@@ -426,7 +424,7 @@ class LoginController extends Controller
|
||||
|
||||
if (Google2FA::verifyKey($user->two_factor_secret, $secret)) {
|
||||
$user->two_factor_enrolled = 1;
|
||||
$user->save();
|
||||
$user->saveQuietly();
|
||||
$request->session()->put('2fa_authed', $user->id);
|
||||
|
||||
return redirect()->route('home')->with('success', 'You are logged in!');
|
||||
|
||||
@@ -56,10 +56,11 @@ class ComponentCheckinController extends Controller
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(Request $request, $component_asset_id)
|
||||
public function store(Request $request, $component_asset_id, $backto = null)
|
||||
{
|
||||
if ($component_assets = DB::table('components_assets')->find($component_asset_id)) {
|
||||
if (is_null($component = Component::find($component_assets->component_id))) {
|
||||
|
||||
return redirect()->route('components.index')->with('error',
|
||||
trans('admin/components/message.not_found'));
|
||||
}
|
||||
@@ -95,6 +96,10 @@ class ComponentCheckinController extends Controller
|
||||
$asset = Asset::find($component_assets->asset_id);
|
||||
|
||||
event(new CheckoutableCheckedIn($component, $asset, Auth::user(), $request->input('note'), Carbon::now()));
|
||||
if ($backto == 'asset'){
|
||||
return redirect()->route('hardware.show', $asset->id)->with('success',
|
||||
trans('admin/components/message.checkin.success'));
|
||||
}
|
||||
|
||||
return redirect()->route('components.index')->with('success',
|
||||
trans('admin/components/message.checkin.success'));
|
||||
|
||||
@@ -20,25 +20,38 @@ class ComponentCheckoutController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ComponentCheckoutController::store() method that stores the data.
|
||||
* @since [v3.0]
|
||||
* @param int $componentId
|
||||
* @param int $id
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create($componentId)
|
||||
public function create($id)
|
||||
{
|
||||
// Check if the component exists
|
||||
if (is_null($component = Component::find($componentId))) {
|
||||
// Redirect to the component management page with error
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
}
|
||||
$this->authorize('checkout', $component);
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($component->numRemaining() <= 0){
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.checkout.unavailable'));
|
||||
if ($component = Component::find($id)) {
|
||||
|
||||
$this->authorize('checkout', $component);
|
||||
|
||||
// Make sure the category is valid
|
||||
if ($component->category) {
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($component->numRemaining() <= 0){
|
||||
return redirect()->route('components.index')
|
||||
->with('error', trans('admin/components/message.checkout.unavailable'));
|
||||
}
|
||||
|
||||
// Return the checkout view
|
||||
return view('components/checkout', compact('component'));
|
||||
}
|
||||
|
||||
// Invalid category
|
||||
return redirect()->route('components.edit', ['component' => $component->id])
|
||||
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.component')]));
|
||||
}
|
||||
|
||||
return view('components/checkout', compact('component'));
|
||||
// Not found
|
||||
return redirect()->route('components.index')->with('error', trans('admin/components/message.not_found'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -142,7 +142,7 @@ class ComponentsFilesController extends Controller
|
||||
$this->authorize('view', $component);
|
||||
$this->authorize('components.files', $component);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Consumables;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -18,25 +19,38 @@ class ConsumableCheckoutController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see ConsumableCheckoutController::store() method that stores the data.
|
||||
* @since [v1.0]
|
||||
* @param int $consumableId
|
||||
* @param int $id
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create($consumableId)
|
||||
public function create($id)
|
||||
{
|
||||
|
||||
if (is_null($consumable = Consumable::with('users')->find($consumableId))) {
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
if ($consumable = Consumable::with('users')->find($id)) {
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
|
||||
// Make sure the category is valid
|
||||
if ($consumable->category) {
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($consumable->numRemaining() <= 0){
|
||||
return redirect()->route('consumables.index')
|
||||
->with('error', trans('admin/consumables/message.checkout.unavailable'));
|
||||
}
|
||||
|
||||
// Return the checkout view
|
||||
return view('consumables/checkout', compact('consumable'));
|
||||
}
|
||||
|
||||
// Invalid category
|
||||
return redirect()->route('consumables.edit', ['consumable' => $consumable->id])
|
||||
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.consumable')]));
|
||||
}
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($consumable->numRemaining() <= 0){
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
|
||||
}
|
||||
// Not found
|
||||
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.does_not_exist'));
|
||||
|
||||
$this->authorize('checkout', $consumable);
|
||||
|
||||
return view('consumables/checkout', compact('consumable'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -140,7 +140,7 @@ class ConsumablesFilesController extends Controller
|
||||
$this->authorize('view', $consumable);
|
||||
$this->authorize('consumables.files', $consumable);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ class CustomFieldsController extends Controller
|
||||
"display_in_user_view" => $display_in_user_view,
|
||||
"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()
|
||||
]);
|
||||
|
||||
@@ -267,6 +268,7 @@ class CustomFieldsController extends Controller
|
||||
$field->display_in_user_view = $display_in_user_view;
|
||||
$field->auto_add_to_fieldsets = $request->get("auto_add_to_fieldsets", 0);
|
||||
$field->show_in_listview = $request->get("show_in_listview", 0);
|
||||
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
|
||||
|
||||
if ($request->get('format') == 'CUSTOM REGEX') {
|
||||
$field->format = e($request->get('custom_format'));
|
||||
|
||||
@@ -7,8 +7,10 @@ use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Labels\Label;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use App\View\Label as LabelView;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -31,20 +33,22 @@ class LabelsController extends Controller
|
||||
|
||||
$exampleAsset->id = 999999;
|
||||
$exampleAsset->name = 'JEN-867-5309';
|
||||
$exampleAsset->asset_tag = 'TCA-00001';
|
||||
$exampleAsset->asset_tag = '100001';
|
||||
$exampleAsset->serial = 'SN9876543210';
|
||||
$exampleAsset->asset_eol_date = '2025-01-01';
|
||||
$exampleAsset->order_number = '12345';
|
||||
$exampleAsset->purchase_date = '2023-01-01';
|
||||
$exampleAsset->status_id = 1;
|
||||
|
||||
$exampleAsset->company = new Company();
|
||||
$exampleAsset->company->id = 999999;
|
||||
$exampleAsset->company->name = 'Test Company Limited';
|
||||
$exampleAsset->company->image = 'company-image-test.png';
|
||||
$exampleAsset->company = new Company([
|
||||
'name' => 'Test Company Limited',
|
||||
'phone' => '1-555-555-5555',
|
||||
'email' => 'company@example.com',
|
||||
]);
|
||||
|
||||
$exampleAsset->assignedto = new User();
|
||||
$exampleAsset->assignedto->id = 999999;
|
||||
$exampleAsset->assignedto->first_name = 'Test';
|
||||
$exampleAsset->assignedto->last_name = 'Person';
|
||||
$exampleAsset->assignedto->username = 'Test.Person';
|
||||
$exampleAsset->assignedto->employee_num = '0123456789';
|
||||
$exampleAsset->setRelation('assignedTo', new User(['first_name' => 'Luke', 'last_name' => 'Skywalker']));
|
||||
$exampleAsset->defaultLoc = new Location(['name' => 'Building 1', 'phone' => '1-555-555-5555']);
|
||||
$exampleAsset->location = new Location(['name' => 'Building 2', 'phone' => '1-555-555-5555']);
|
||||
|
||||
$exampleAsset->model = new AssetModel();
|
||||
$exampleAsset->model->id = 999999;
|
||||
@@ -53,6 +57,10 @@ class LabelsController extends Controller
|
||||
$exampleAsset->model->manufacturer = new Manufacturer();
|
||||
$exampleAsset->model->manufacturer->id = 999999;
|
||||
$exampleAsset->model->manufacturer->name = 'Test Manufacturing Inc.';
|
||||
$exampleAsset->model->manufacturer->support_email = 'support@test.com';
|
||||
$exampleAsset->model->manufacturer->support_phone = '1-555-555-5555';
|
||||
$exampleAsset->model->manufacturer->support_url = 'https://example.com';
|
||||
$exampleAsset->supplier = new Supplier(['name' => 'Test Company Limited']);
|
||||
$exampleAsset->model->category = new Category();
|
||||
$exampleAsset->model->category->id = 999999;
|
||||
$exampleAsset->model->category->name = 'Test Category';
|
||||
|
||||
@@ -76,7 +76,7 @@ class LicenseCheckinController extends Controller
|
||||
|
||||
// Declare the rules for the form validation
|
||||
$rules = [
|
||||
'note' => 'string|nullable',
|
||||
'notes' => 'string|nullable',
|
||||
];
|
||||
|
||||
// Create a new validator instance from our validation rules
|
||||
@@ -97,10 +97,11 @@ class LicenseCheckinController extends Controller
|
||||
// Update the asset data
|
||||
$licenseSeat->assigned_to = null;
|
||||
$licenseSeat->asset_id = null;
|
||||
$licenseSeat->notes = $request->input('notes');
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, Auth::user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, Auth::user(), $request->input('notes')));
|
||||
|
||||
if ($backTo == 'user') {
|
||||
return redirect()->route('users.show', $return_to->id)->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Licenses;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\LicenseCheckoutRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
@@ -21,23 +22,35 @@ class LicenseCheckoutController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $licenseId
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create($licenseId)
|
||||
public function create($id)
|
||||
{
|
||||
// Check that the license is valid
|
||||
if ($license = License::find($licenseId)) {
|
||||
|
||||
if ($license = License::find($id)) {
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
// If the license is valid, check that there is an available seat
|
||||
if ($license->avail_seats_count < 1) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
|
||||
if ($license->category) {
|
||||
|
||||
// Make sure there is at least one available to checkout
|
||||
if ($license->availCount()->count() < 1){
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats'));
|
||||
}
|
||||
|
||||
// Return the checkout view
|
||||
return view('licenses/checkout', compact('license'));
|
||||
}
|
||||
return view('licenses/checkout', compact('license'));
|
||||
|
||||
// Invalid category
|
||||
return redirect()->route('licenses.edit', ['license' => $license->id])
|
||||
->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.license')]));
|
||||
|
||||
}
|
||||
|
||||
// Not found
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||
|
||||
|
||||
@@ -63,6 +76,7 @@ class LicenseCheckoutController extends Controller
|
||||
|
||||
$licenseSeat = $this->findLicenseSeatToCheckout($license, $seatId);
|
||||
$licenseSeat->user_id = Auth::id();
|
||||
$licenseSeat->notes = $request->input('notes');
|
||||
|
||||
|
||||
$checkoutMethod = 'checkoutTo'.ucwords(request('checkout_to_type'));
|
||||
@@ -104,7 +118,7 @@ class LicenseCheckoutController extends Controller
|
||||
$licenseSeat->assigned_to = $target->assigned_to;
|
||||
}
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note')));
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('notes')));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -121,7 +135,7 @@ class LicenseCheckoutController extends Controller
|
||||
$licenseSeat->assigned_to = request('assigned_to');
|
||||
|
||||
if ($licenseSeat->save()) {
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note')));
|
||||
event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('notes')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class LicenseFilesController extends Controller
|
||||
$this->authorize('view', $license);
|
||||
$this->authorize('licenses.files', $license);
|
||||
|
||||
if (! $log = Actionlog::find($fileId)) {
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -218,22 +222,37 @@ class ManufacturersController extends Controller
|
||||
* @return Redirect
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function restore($manufacturers_id)
|
||||
public function restore($id)
|
||||
{
|
||||
$this->authorize('create', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::onlyTrashed()->where('id', $manufacturers_id)->first();
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
|
||||
if ($manufacturer) {
|
||||
if ($manufacturer = Manufacturer::withTrashed()->find($id)) {
|
||||
|
||||
if ($manufacturer->deleted_at == '') {
|
||||
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.manufacturer')]));
|
||||
}
|
||||
|
||||
// Not sure why this is necessary - it shouldn't fail validation here, but it fails without this, so....
|
||||
$manufacturer->setValidating(false);
|
||||
if ($manufacturer->restore()) {
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = Manufacturer::class;
|
||||
$logaction->item_id = $manufacturer->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
// Redirect them to the deleted page if there are more, otherwise the section index
|
||||
$deleted_manufacturers = Manufacturer::onlyTrashed()->count();
|
||||
if ($deleted_manufacturers > 0) {
|
||||
return redirect()->back()->with('success', trans('admin/manufacturers/message.success.restored'));
|
||||
}
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.restore.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'Could not restore.');
|
||||
// Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
|
||||
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.manufacturer'), 'error' => $manufacturer->getErrors()->first()]));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ class ProfileController extends Controller
|
||||
];
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
|
||||
$validator->after(function ($validator) use ($request, $user) {
|
||||
if (! Hash::check($request->input('current_password'), $user->password)) {
|
||||
$validator->errors()->add('current_password', trans('validation.custom.hashed_pass'));
|
||||
@@ -159,12 +160,14 @@ class ProfileController extends Controller
|
||||
});
|
||||
|
||||
if (! $validator->fails()) {
|
||||
$user->password = Hash::make($request->input('password'));
|
||||
$user->save();
|
||||
|
||||
$user->password = Hash::make($request->input('password'));
|
||||
// We have to use saveQuietly here because for some reason this method was calling the User Oserver twice :(
|
||||
$user->saveQuietly();
|
||||
|
||||
// Log the user out of other devices
|
||||
Auth::logoutOtherDevices($request->input('password'));
|
||||
return redirect()->route('account.password.index')->with('success', 'Password updated!');
|
||||
return redirect()->route('account')->with('success', trans('passwords.password_change'));
|
||||
|
||||
}
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
|
||||
@@ -23,6 +23,7 @@ use Input;
|
||||
use League\Csv\Reader;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use League\Csv\EscapeFormula;
|
||||
use App\Http\Requests\CustomAssetReportRequest;
|
||||
|
||||
|
||||
/**
|
||||
@@ -246,6 +247,9 @@ class ReportsController extends Controller
|
||||
trans('general.action'),
|
||||
trans('general.type'),
|
||||
trans('general.item'),
|
||||
trans('general.license_serial'),
|
||||
trans('general.model_name'),
|
||||
trans('general.model_no'),
|
||||
'To',
|
||||
trans('general.notes'),
|
||||
'Changed',
|
||||
@@ -288,6 +292,9 @@ class ReportsController extends Controller
|
||||
$actionlog->present()->actionType(),
|
||||
e($actionlog->itemType()),
|
||||
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
|
||||
($actionlog->item->serial) ? $actionlog->item->serial : null,
|
||||
($actionlog->item->model) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null,
|
||||
($actionlog->item->model) ? $actionlog->item->model->model_number : null,
|
||||
$target_name,
|
||||
($actionlog->note) ? e($actionlog->note) : '',
|
||||
$actionlog->log_meta,
|
||||
@@ -403,11 +410,12 @@ class ReportsController extends Controller
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function postCustom(Request $request)
|
||||
public function postCustom(CustomAssetReportRequest $request)
|
||||
{
|
||||
ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes
|
||||
$this->authorize('reports.view');
|
||||
|
||||
|
||||
\Debugbar::disable();
|
||||
$customfields = CustomField::get();
|
||||
$response = new StreamedResponse(function () use ($customfields, $request) {
|
||||
@@ -526,6 +534,30 @@ class ReportsController extends Controller
|
||||
$header[] = trans('admin/users/table.title');
|
||||
}
|
||||
|
||||
if ($request->filled('phone')) {
|
||||
$header[] = trans('admin/users/table.phone');
|
||||
}
|
||||
|
||||
if ($request->filled('user_address')) {
|
||||
$header[] = trans('admin/reports/general.custom_export.user_address');
|
||||
}
|
||||
|
||||
if ($request->filled('user_city')) {
|
||||
$header[] = trans('admin/reports/general.custom_export.user_city');
|
||||
}
|
||||
|
||||
if ($request->filled('user_state')) {
|
||||
$header[] = trans('admin/reports/general.custom_export.user_state');
|
||||
}
|
||||
|
||||
if ($request->filled('user_country')) {
|
||||
$header[] = trans('admin/reports/general.custom_export.user_country');
|
||||
}
|
||||
|
||||
if ($request->filled('user_zip')) {
|
||||
$header[] = trans('admin/reports/general.custom_export.user_zip');
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$header[] = trans('general.status');
|
||||
}
|
||||
@@ -645,7 +677,7 @@ class ReportsController extends Controller
|
||||
if (($request->filled('created_start')) && ($request->filled('created_end'))) {
|
||||
$created_start = \Carbon::parse($request->input('created_start'))->startOfDay();
|
||||
$created_end = \Carbon::parse($request->input('created_end'))->endOfDay();
|
||||
|
||||
|
||||
$assets->whereBetween('assets.created_at', [$created_start, $created_end]);
|
||||
}
|
||||
if (($request->filled('checkout_date_start')) && ($request->filled('checkout_date_end'))) {
|
||||
@@ -656,22 +688,22 @@ class ReportsController extends Controller
|
||||
}
|
||||
|
||||
if (($request->filled('checkin_date_start'))) {
|
||||
$assets->whereBetween('last_checkin', [
|
||||
Carbon::parse($request->input('checkin_date_start'))->startOfDay(),
|
||||
// use today's date is `checkin_date_end` is not provided
|
||||
Carbon::parse($request->input('checkin_date_end', now()))->endOfDay(),
|
||||
]);
|
||||
$assets->whereBetween('last_checkin', [
|
||||
Carbon::parse($request->input('checkin_date_start'))->startOfDay(),
|
||||
// use today's date is `checkin_date_end` is not provided
|
||||
Carbon::parse($request->input('checkin_date_end', now()))->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('last_audit_start')) && ($request->filled('last_audit_end'))) {
|
||||
$last_audit_start = \Carbon::parse($request->input('last_audit_start'))->startOfDay();
|
||||
$last_audit_end = \Carbon::parse($request->input('last_audit_end'))->endOfDay();
|
||||
$last_audit_start = \Carbon::parse($request->input('last_audit_start'))->startOfDay();
|
||||
$last_audit_end = \Carbon::parse($request->input('last_audit_end'))->endOfDay();
|
||||
|
||||
$assets->whereBetween('assets.last_audit_date', [$last_audit_start, $last_audit_end]);
|
||||
$assets->whereBetween('assets.last_audit_date', [$last_audit_start, $last_audit_end]);
|
||||
}
|
||||
|
||||
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
|
||||
@@ -742,7 +774,7 @@ class ReportsController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('eol')) {
|
||||
$row[] = ($asset->purchase_date != '') ? $asset->present()->eol_date() : '';
|
||||
$row[] = ($asset->asset_eol_date) ? $asset->asset_eol_date : '';
|
||||
}
|
||||
|
||||
if ($request->filled('order')) {
|
||||
@@ -826,6 +858,54 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('phone')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->phone : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('user_address')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->address : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('user_city')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->city : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('user_state')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->state : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('user_country')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->country : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('user_zip')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = ($asset->assignedto) ? $asset->assignedto->zip : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
|
||||
}
|
||||
@@ -1043,27 +1123,34 @@ class ReportsController extends Controller
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @version v1.0
|
||||
*/
|
||||
public function sentAssetAcceptanceReminder($acceptanceId = null)
|
||||
public function sentAssetAcceptanceReminder(Request $request)
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
|
||||
if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
|
||||
if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
|
||||
\Log::debug('No pending acceptances');
|
||||
// Redirect to the unaccepted assets report page with error
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
|
||||
$assetItem = $acceptance->checkoutable;
|
||||
|
||||
\Log::debug(print_r($assetItem, true));
|
||||
|
||||
if (is_null($acceptance->created_at)){
|
||||
\Log::debug('No acceptance created_at');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
} else {
|
||||
$logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
|
||||
|
||||
if ($logItem_res->isEmpty()){
|
||||
\Log::debug('Acceptance date mismatch');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$logItem = $logItem_res[0];
|
||||
}
|
||||
|
||||
if(!$assetItem->assignedTo->locale){
|
||||
if (!$assetItem->assignedTo->locale){
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$assetItem->assignedTo,
|
||||
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Helpers\StorageHelper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SettingsSamlRequest;
|
||||
use App\Http\Requests\SetupUserRequest;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Group;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Asset;
|
||||
@@ -26,7 +27,7 @@ use Response;
|
||||
use App\Http\Requests\SlackSettingsRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Validator;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Settings for
|
||||
@@ -809,9 +810,10 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function getLabels()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
return view('settings.labels', compact('setting'));
|
||||
return view('settings.labels', [
|
||||
'setting' => Setting::getSettings(),
|
||||
'customFields' => CustomField::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1248,13 +1250,11 @@ class SettingsController extends Controller
|
||||
if (!$request->hasFile('file')) {
|
||||
return redirect()->route('settings.backups.index')->with('error', 'No file uploaded');
|
||||
} else {
|
||||
|
||||
$max_file_size = Helper::file_upload_max_size();
|
||||
|
||||
$rules = [
|
||||
$validator = Validator::make($request->all(), [
|
||||
'file' => 'required|mimes:zip|max:'.$max_file_size,
|
||||
];
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
]);
|
||||
|
||||
if ($validator->passes()) {
|
||||
|
||||
@@ -1265,7 +1265,7 @@ class SettingsController extends Controller
|
||||
return redirect()->route('settings.backups.index')->with('success', 'File uploaded');
|
||||
}
|
||||
|
||||
return redirect()->route('settings.backups.index')->withErrors($request->getErrors());
|
||||
return redirect()->route('settings.backups.index')->withErrors($validator);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -125,10 +125,26 @@ class BulkUsersController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the user wants to actually blank out the values vs skip them
|
||||
*/
|
||||
if ($request->input('null_location_id')=='1') {
|
||||
$this->update_array['location_id'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_department_id')=='1') {
|
||||
$this->update_array['department_id'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_manager_id')=='1') {
|
||||
$this->update_array['manager_id'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_company_id')=='1') {
|
||||
$this->update_array['company_id'] = null;
|
||||
}
|
||||
|
||||
|
||||
if (! $manager_conflict) {
|
||||
$this->conditionallyAddItem('manager_id');
|
||||
}
|
||||
|
||||
@@ -49,15 +49,19 @@ class LDAPImportController extends Controller
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
// Call Artisan LDAP import command.
|
||||
$location_id = $request->input('location_id');
|
||||
Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]);
|
||||
|
||||
Artisan::call('snipeit:ldap-sync', ['--location_id' => $request->input('location_id'), '--json_summary' => true]);
|
||||
|
||||
// Collect and parse JSON summary.
|
||||
$ldap_results_json = Artisan::output();
|
||||
$ldap_results = json_decode($ldap_results_json, true);
|
||||
if (!$ldap_results) {
|
||||
return redirect()->back()->withInput()->with('error', trans('general.no_results'));
|
||||
}
|
||||
|
||||
// Direct user to appropriate status page.
|
||||
if ($ldap_results['error']) {
|
||||
|
||||
return redirect()->back()->withInput()->with('error', $ldap_results['error_message']);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,11 @@ class UserFilesController extends Controller
|
||||
*/
|
||||
public function show($userId = null, $fileId = null)
|
||||
{
|
||||
|
||||
if (empty($fileId)) {
|
||||
return redirect()->route('users.show')->with('error', 'Invalid file request');
|
||||
}
|
||||
|
||||
$user = User::find($userId);
|
||||
|
||||
// the license is valid
|
||||
@@ -143,18 +148,20 @@ class UserFilesController extends Controller
|
||||
|
||||
$this->authorize('view', $user);
|
||||
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
|
||||
|
||||
// Display the file inline
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
|
||||
// Display the file inline
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return Storage::download('private_uploads/users/'.$log->filename);
|
||||
}
|
||||
|
||||
return Storage::download('private_uploads/users/'.$log->filename);
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found'));
|
||||
}
|
||||
|
||||
// Redirect to the user management page if the user doesn't exist
|
||||
|
||||
@@ -7,10 +7,10 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\UserNotFoundException;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Group;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
@@ -385,18 +385,35 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function getRestore($id = null)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
// Get user information
|
||||
if (! User::onlyTrashed()->find($id)) {
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/messages.user_not_found'));
|
||||
if ($user = User::withTrashed()->find($id)) {
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
if ($user->deleted_at == '') {
|
||||
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.user')]));
|
||||
}
|
||||
|
||||
if ($user->restore()) {
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_type = User::class;
|
||||
$logaction->item_id = $user->id;
|
||||
$logaction->created_at = date('Y-m-d H:i:s');
|
||||
$logaction->user_id = Auth::user()->id;
|
||||
$logaction->logaction('restore');
|
||||
|
||||
// Redirect them to the deleted page if there are more, otherwise the section index
|
||||
$deleted_users = User::onlyTrashed()->count();
|
||||
if ($deleted_users > 0) {
|
||||
return redirect()->back()->with('success', trans('admin/users/message.success.restored'));
|
||||
}
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.restored'));
|
||||
|
||||
}
|
||||
|
||||
// Check validation to make sure we're not restoring a user with the same username as an existing user
|
||||
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()]));
|
||||
}
|
||||
|
||||
// Restore the user
|
||||
if (User::withTrashed()->where('id', $id)->restore()) {
|
||||
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.restored'));
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', 'User could not be restored.');
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -275,6 +275,7 @@ class Importer extends Component
|
||||
'license_email' => trans('admin/licenses/form.to_email'),
|
||||
'license_name' => trans('admin/licenses/form.to_name'),
|
||||
'purchase_order' => trans('admin/licenses/form.purchase_order'),
|
||||
'order_number' => trans('general.order_number'),
|
||||
'reassignable' => trans('admin/licenses/form.reassignable'),
|
||||
'seats' => trans('admin/licenses/form.seats'),
|
||||
'notes' => trans('general.notes'),
|
||||
@@ -484,8 +485,17 @@ class Importer extends Component
|
||||
|
||||
public function selectFile($id)
|
||||
{
|
||||
$this->clearMessage();
|
||||
|
||||
$this->activeFile = Import::find($id);
|
||||
|
||||
if (!$this->activeFile) {
|
||||
$this->message = trans('admin/hardware/message.import.file_missing');
|
||||
$this->message_type = 'danger';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->field_map = null;
|
||||
foreach($this->activeFile->header_row as $element) {
|
||||
if(isset($this->activeFile->field_map[$element])) {
|
||||
@@ -520,6 +530,12 @@ class Importer extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function clearMessage()
|
||||
{
|
||||
$this->message = null;
|
||||
$this->message_type = null;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->files = Import::orderBy('id','desc')->get(); //HACK - slows down renders.
|
||||
|
||||
@@ -12,7 +12,7 @@ class SlackSettingsForm extends Component
|
||||
public $webhook_endpoint;
|
||||
public $webhook_channel;
|
||||
public $webhook_botname;
|
||||
public $isDisabled ='' ;
|
||||
public $isDisabled ='disabled' ;
|
||||
public $webhook_name;
|
||||
public $webhook_link;
|
||||
public $webhook_placeholder;
|
||||
@@ -22,11 +22,17 @@ class SlackSettingsForm extends Component
|
||||
|
||||
public Setting $setting;
|
||||
|
||||
public $webhook_endpoint_rules;
|
||||
|
||||
|
||||
protected $rules = [
|
||||
'webhook_endpoint' => 'url|required_with:webhook_channel|starts_with:https://hooks.slack.com/services|nullable',
|
||||
'webhook_endpoint' => 'required_with:webhook_channel|starts_with:http://,https://,ftp://,irc://,https://hooks.slack.com/services/|url|nullable',
|
||||
'webhook_channel' => 'required_with:webhook_endpoint|starts_with:#|nullable',
|
||||
'webhook_botname' => 'string|nullable',
|
||||
];
|
||||
public $messages = [
|
||||
'webhook_endpoint.starts_with' => 'your webhook endpoint should begin with http://, https:// or other protocol.',
|
||||
];
|
||||
|
||||
public function mount() {
|
||||
$this->webhook_text= [
|
||||
@@ -55,9 +61,7 @@ class SlackSettingsForm extends Component
|
||||
$this->webhook_botname = $this->setting->webhook_botname;
|
||||
$this->webhook_options = $this->setting->webhook_selected;
|
||||
|
||||
if($this->setting->webhook_selected == 'general'){
|
||||
$this->isDisabled='';
|
||||
}
|
||||
|
||||
if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){
|
||||
$this->isDisabled= '';
|
||||
}
|
||||
@@ -65,9 +69,8 @@ class SlackSettingsForm extends Component
|
||||
}
|
||||
public function updated($field) {
|
||||
|
||||
if($this->webhook_selected != 'general') {
|
||||
$this->validateOnly($field, $this->rules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function updatedWebhookSelected() {
|
||||
@@ -82,7 +85,6 @@ class SlackSettingsForm extends Component
|
||||
}
|
||||
|
||||
private function isButtonDisabled() {
|
||||
if($this->webhook_selected == 'slack') {
|
||||
if (empty($this->webhook_endpoint)) {
|
||||
$this->isDisabled = 'disabled';
|
||||
$this->save_button = trans('admin/settings/general.webhook_presave');
|
||||
@@ -91,8 +93,6 @@ class SlackSettingsForm extends Component
|
||||
$this->isDisabled = 'disabled';
|
||||
$this->save_button = trans('admin/settings/general.webhook_presave');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
@@ -108,6 +108,7 @@ class SlackSettingsForm extends Component
|
||||
'defaults' => [
|
||||
'exceptions' => false,
|
||||
],
|
||||
'allow_redirects' => false,
|
||||
]);
|
||||
|
||||
$payload = json_encode(
|
||||
@@ -116,18 +117,23 @@ class SlackSettingsForm extends Component
|
||||
'text' => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
|
||||
'username' => e($this->webhook_botname),
|
||||
'icon_emoji' => ':heart:',
|
||||
|
||||
]);
|
||||
|
||||
try {
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload]);
|
||||
|
||||
$webhook->post($this->webhook_endpoint, ['body' => $payload]);
|
||||
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
|
||||
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
|
||||
}
|
||||
$this->isDisabled='';
|
||||
$this->save_button = trans('general.save');
|
||||
return session()->flash('success' , 'Your '.$this->webhook_name.' Integration works!');
|
||||
return session()->flash('success' , trans('admin/settings/message.webhook.success', ['webhook_name' => $this->webhook_name]));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
$this->isDisabled= 'disabled';
|
||||
$this->isDisabled='disabled';
|
||||
$this->save_button = trans('admin/settings/general.webhook_presave');
|
||||
return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name]));
|
||||
}
|
||||
|
||||
@@ -158,9 +164,7 @@ class SlackSettingsForm extends Component
|
||||
if (Helper::isDemoMode()) {
|
||||
session()->flash('error',trans('general.feature_disabled'));
|
||||
} else {
|
||||
if ($this->webhook_selected != 'general') {
|
||||
$this->validate($this->rules);
|
||||
}
|
||||
$this->validate($this->rules);
|
||||
|
||||
$this->setting->webhook_selected = $this->webhook_selected;
|
||||
$this->setting->webhook_endpoint = $this->webhook_endpoint;
|
||||
|
||||
46
app/Http/Requests/CustomAssetReportRequest.php
Normal file
46
app/Http/Requests/CustomAssetReportRequest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
class CustomAssetReportRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'purchase_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'purchase_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'created_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'created_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'checkout_date_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'checkout_date_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'expected_checkin_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'expected_checkin_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'checkin_date_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'checkin_date_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'last_audit_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'last_audit_end' => 'date|date_format:Y-m-d|nullable',
|
||||
'next_audit_start' => 'date|date_format:Y-m-d|nullable',
|
||||
'next_audit_end' => 'date|date_format:Y-m-d|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
{
|
||||
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
|
||||
}
|
||||
}
|
||||
40
app/Http/Requests/StoreAssetRequest.php
Normal file
40
app/Http/Requests/StoreAssetRequest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', new Asset);
|
||||
}
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = array_merge(
|
||||
(new Asset)->getRules(),
|
||||
parent::rules(),
|
||||
);
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Traits;
|
||||
|
||||
use App\Models\Setting;
|
||||
|
||||
trait UniqueSerialTrait
|
||||
{
|
||||
/**
|
||||
* Prepare a unique_ids rule, adding a model identifier if required.
|
||||
*
|
||||
* @param array $parameters
|
||||
* @param string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareUniqueSerialRule($parameters, $field)
|
||||
{
|
||||
if ($settings = Setting::getSettings()) {
|
||||
if ($settings->unique_serial == '1') {
|
||||
return 'unique_undeleted:'.$this->table.','.$this->getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Company;
|
||||
@@ -10,6 +11,9 @@ use App\Models\Supplier;
|
||||
use App\Models\Location;
|
||||
use App\Models\AssetModel;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class ActionlogsTransformer
|
||||
{
|
||||
@@ -43,9 +47,10 @@ class ActionlogsTransformer
|
||||
public function transformActionlog (Actionlog $actionlog, $settings = null)
|
||||
{
|
||||
$icon = $actionlog->present()->icon();
|
||||
$custom_field = CustomField::all();
|
||||
$custom_fields = CustomField::all();
|
||||
|
||||
if ($actionlog->filename!='') {
|
||||
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
|
||||
$icon = Helper::filetype_icon($actionlog->filename);
|
||||
}
|
||||
|
||||
// This is necessary since we can't escape special characters within a JSON object
|
||||
@@ -55,17 +60,63 @@ class ActionlogsTransformer
|
||||
$clean_meta = [];
|
||||
|
||||
if ($meta_array) {
|
||||
|
||||
foreach ($meta_array as $fieldname => $fieldata) {
|
||||
if( str_starts_with($fieldname, '_snipeit_')){
|
||||
if( $custom_field->where('db_column', '=', $fieldname)->where('field_encrypted', true)){
|
||||
$clean_meta[$fieldname]['old'] = "encrypted";
|
||||
$clean_meta[$fieldname]['new'] = "encrypted";
|
||||
|
||||
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
|
||||
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
|
||||
|
||||
// this is a custom field
|
||||
if (str_starts_with($fieldname, '_snipeit_')) {
|
||||
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
|
||||
if ($custom_field->db_column == $fieldname) {
|
||||
|
||||
if ($custom_field->field_encrypted == '1') {
|
||||
|
||||
// Unset these fields. We need to decrypt them, since even if the decrypted value
|
||||
// didn't change, their value in the DB will, so we have to compare the unencrypted version
|
||||
// to see if the values actually did change
|
||||
unset($clean_meta[$fieldname]);
|
||||
unset($clean_meta[$fieldname]);
|
||||
|
||||
$enc_old = '';
|
||||
$enc_new = '';
|
||||
|
||||
try {
|
||||
$enc_old = \Crypt::decryptString($this->clean_field($fieldata->old));
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Could not decrypt field - maybe the key changed?');
|
||||
}
|
||||
|
||||
try {
|
||||
$enc_new = \Crypt::decryptString($this->clean_field($fieldata->new));
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Could not decrypt field - maybe the key changed?');
|
||||
}
|
||||
|
||||
if ($enc_old != $enc_new) {
|
||||
\Log::debug('custom fields do not match');
|
||||
$clean_meta[$fieldname]['old'] = "************";
|
||||
$clean_meta[$fieldname]['new'] = "************";
|
||||
|
||||
// Display the changes if the user is an admin or superadmin
|
||||
if (Gate::allows('admin')) {
|
||||
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old): '';
|
||||
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
|
||||
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -165,24 +216,42 @@ class ActionlogsTransformer
|
||||
|
||||
|
||||
if(array_key_exists('rtd_location_id',$clean_meta)) {
|
||||
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $location->find($clean_meta['rtd_location_id']['old'])->name : trans('general.unassigned');
|
||||
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $location->find($clean_meta['rtd_location_id']['new'])->name : trans('general.unassigned');
|
||||
|
||||
$oldRtd = $location->find($clean_meta['rtd_location_id']['old']);
|
||||
$oldRtdName = $oldRtd ? e($oldRtd->name) : trans('general.deleted');
|
||||
|
||||
$newRtd = $location->find($clean_meta['rtd_location_id']['new']);
|
||||
$newRtdName = $newRtd ? e($newRtd->name) : trans('general.deleted');
|
||||
|
||||
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $oldRtdName : '';
|
||||
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $newRtdName : '';
|
||||
$clean_meta['Default Location'] = $clean_meta['rtd_location_id'];
|
||||
unset($clean_meta['rtd_location_id']);
|
||||
}
|
||||
if(array_key_exists('location_id', $clean_meta)) {
|
||||
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ".$location->find($clean_meta['location_id']['old'])->name : trans('general.unassigned');
|
||||
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ".$location->find($clean_meta['location_id']['new'])->name : trans('general.unassigned');
|
||||
|
||||
|
||||
if (array_key_exists('location_id', $clean_meta)) {
|
||||
|
||||
$oldLocation = $location->find($clean_meta['location_id']['old']);
|
||||
$oldLocationName = $oldLocation ? e($oldLocation->name) : trans('general.deleted');
|
||||
|
||||
$newLocation = $location->find($clean_meta['location_id']['new']);
|
||||
$newLocationName = $newLocation ? e($newLocation->name) : trans('general.deleted');
|
||||
|
||||
|
||||
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ". $oldLocationName : '';
|
||||
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ". $newLocationName : '';
|
||||
$clean_meta['Current Location'] = $clean_meta['location_id'];
|
||||
unset($clean_meta['location_id']);
|
||||
}
|
||||
|
||||
if(array_key_exists('model_id', $clean_meta)) {
|
||||
|
||||
$oldModel = $model->find($clean_meta['model_id']['old']);
|
||||
$oldModelName = $oldModel->name ?? trans('admin/models/message.deleted');
|
||||
$oldModelName = $oldModel ? e($oldModel->name) : trans('admin/models/message.deleted');
|
||||
|
||||
$newModel = $model->find($clean_meta['model_id']['new']);
|
||||
$newModelName = $newModel->name ?? trans('admin/models/message.deleted');
|
||||
$newModelName = $newModel ? e($newModel->name) : trans('admin/models/message.deleted');
|
||||
|
||||
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName;
|
||||
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */
|
||||
@@ -193,10 +262,10 @@ class ActionlogsTransformer
|
||||
if(array_key_exists('company_id', $clean_meta)) {
|
||||
|
||||
$oldCompany = $company->find($clean_meta['company_id']['old']);
|
||||
$oldCompanyName = $oldCompany->name ?? trans('admin/companies/message.deleted');
|
||||
$oldCompanyName = $oldCompany ? e($oldCompany->name) : trans('admin/company/message.deleted');
|
||||
|
||||
$newCompany = $company->find($clean_meta['company_id']['new']);
|
||||
$newCompanyName = $newCompany->name ?? trans('admin/companies/message.deleted');
|
||||
$newCompanyName = $newCompany ? e($newCompany->name) : trans('admin/company/message.deleted');
|
||||
|
||||
$clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned');
|
||||
$clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ". $newCompanyName : trans('general.unassigned');
|
||||
@@ -206,10 +275,10 @@ class ActionlogsTransformer
|
||||
if(array_key_exists('supplier_id', $clean_meta)) {
|
||||
|
||||
$oldSupplier = $supplier->find($clean_meta['supplier_id']['old']);
|
||||
$oldSupplierName = $oldSupplier->name ?? trans('admin/suppliers/message.deleted');
|
||||
$oldSupplierName = $oldSupplier ? e($oldSupplier->name) : trans('admin/suppliers/message.deleted');
|
||||
|
||||
$newSupplier = $supplier->find($clean_meta['supplier_id']['new']);
|
||||
$newSupplierName = $newSupplier->name ?? trans('admin/suppliers/message.deleted');
|
||||
$newSupplierName = $newSupplier ? e($newSupplier->name) : trans('admin/suppliers/message.deleted');
|
||||
|
||||
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned');
|
||||
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned');
|
||||
|
||||
@@ -73,7 +73,7 @@ class AssetModelsTransformer
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => (Gate::allows('update', AssetModel::class) && ($assetmodel->deleted_at == '')),
|
||||
'delete' => (Gate::allows('delete', AssetModel::class) && ($assetmodel->assets_count == 0)),
|
||||
'delete' => $assetmodel->isDeletable(),
|
||||
'clone' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at == '')),
|
||||
'restore' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at != '')),
|
||||
];
|
||||
|
||||
@@ -7,7 +7,8 @@ use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Auth;
|
||||
|
||||
class AssetsTransformer
|
||||
{
|
||||
@@ -38,7 +39,7 @@ class AssetsTransformer
|
||||
'byod' => ($asset->byod ? true : false),
|
||||
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'eol' => (($asset->model) && ($asset->model->eol != '')) ? $asset->model->eol : null,
|
||||
'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date).' months' : null,
|
||||
'asset_eol_date' => ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date') : null,
|
||||
'status_label' => ($asset->assetstatus) ? [
|
||||
'id' => (int) $asset->assetstatus->id,
|
||||
@@ -146,7 +147,7 @@ class AssetsTransformer
|
||||
'clone' => Gate::allows('create', Asset::class) ? true : false,
|
||||
'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false,
|
||||
'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false,
|
||||
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class)) ? true : false,
|
||||
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class) && ($asset->deleted_at == '')) ? true : false,
|
||||
];
|
||||
|
||||
|
||||
@@ -231,6 +232,29 @@ class AssetsTransformer
|
||||
'assigned_to_self' => ($asset->assigned_to == \Auth::user()->id),
|
||||
];
|
||||
|
||||
if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) {
|
||||
$fields_array = [];
|
||||
|
||||
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')) {
|
||||
|
||||
$value = $asset->{$field->db_column};
|
||||
if (($field->format == 'DATE') && (!is_null($value)) && ($value != '')) {
|
||||
$value = Helper::getFormattedDateObject($value, 'date', false);
|
||||
}
|
||||
|
||||
$fields_array[$field->db_column] = e($value);
|
||||
}
|
||||
|
||||
$array['custom_fields'] = $fields_array;
|
||||
}
|
||||
} else {
|
||||
$array['custom_fields'] = new \stdClass; // HACK to force generation of empty object instead of empty list
|
||||
}
|
||||
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'cancel' => ($asset->isRequestedBy(\Auth::user())) ? true : false,
|
||||
'request' => ($asset->isRequestedBy(\Auth::user())) ? false : true,
|
||||
|
||||
@@ -45,6 +45,7 @@ class LicenseSeatsTransformer
|
||||
'name'=> e($seat->location()->name),
|
||||
] : null,
|
||||
'reassignable' => (bool) $seat->license->reassignable,
|
||||
'notes' => e($seat->notes),
|
||||
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
|
||||
];
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class UsersTransformer
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')),
|
||||
'delete' => (Gate::allows('delete', User::class) && ($user->assets_count == 0) && ($user->licenses_count == 0) && ($user->accessories_count == 0)),
|
||||
'delete' => $user->isDeletable(),
|
||||
'clone' => (Gate::allows('create', User::class) && ($user->deleted_at == '')),
|
||||
'restore' => (Gate::allows('create', User::class) && ($user->deleted_at != '')),
|
||||
];
|
||||
|
||||
@@ -34,7 +34,7 @@ class AccessoryImporter extends ItemImporter
|
||||
}
|
||||
|
||||
$this->log('Updating Accessory');
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");
|
||||
$this->item['model_number'] = trim($this->findCsvMatch($row, "model_number"));
|
||||
$accessory->update($this->sanitizeItemForUpdating($accessory));
|
||||
$accessory->save();
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\User;
|
||||
use App\Events\CheckoutableCheckedIn;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class AssetImporter extends ItemImporter
|
||||
{
|
||||
@@ -63,6 +68,7 @@ class AssetImporter extends ItemImporter
|
||||
$asset_tag = Asset::autoincrement_asset();
|
||||
}
|
||||
|
||||
|
||||
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
|
||||
if ($asset) {
|
||||
if (! $this->updating) {
|
||||
@@ -77,13 +83,13 @@ class AssetImporter extends ItemImporter
|
||||
$this->log('No Matching Asset, Creating a new one');
|
||||
$asset = new Asset;
|
||||
}
|
||||
$this->item['notes'] = $this->findCsvMatch($row, 'asset_notes');
|
||||
$this->item['image'] = $this->findCsvMatch($row, 'image');
|
||||
$this->item['requestable'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable')) == 1) ? '1' : 0;
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'asset_notes'));
|
||||
$this->item['image'] = trim($this->findCsvMatch($row, 'image'));
|
||||
$this->item['requestable'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'))) == 1) ? '1' : 0;
|
||||
$asset->requestable = $this->item['requestable'];
|
||||
$this->item['warranty_months'] = intval($this->findCsvMatch($row, 'warranty_months'));
|
||||
$this->item['warranty_months'] = intval(trim($this->findCsvMatch($row, 'warranty_months')));
|
||||
$this->item['model_id'] = $this->createOrFetchAssetModel($row);
|
||||
$this->item['byod'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'byod')) == 1) ? '1' : 0;
|
||||
$this->item['byod'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'byod'))) == 1) ? '1' : 0;
|
||||
|
||||
|
||||
// If no status ID is found
|
||||
@@ -116,12 +122,7 @@ class AssetImporter extends ItemImporter
|
||||
if (isset($this->item['next_audit_date'])) {
|
||||
$item['next_audit_date'] = $this->item['next_audit_date'];
|
||||
}
|
||||
|
||||
$item['asset_eol_date'] = null;
|
||||
if (isset($this->item['asset_eol_date'])) {
|
||||
$item['asset_eol_date'] = $this->item['asset_eol_date'];
|
||||
}
|
||||
|
||||
|
||||
if ($editingAsset) {
|
||||
$asset->update($item);
|
||||
} else {
|
||||
@@ -134,16 +135,22 @@ class AssetImporter extends ItemImporter
|
||||
$asset->{$custom_field} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
|
||||
$asset->logCreate(trans('general.importer.import_note'));
|
||||
$this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||
|
||||
// If we have a target to checkout to, lets do so.
|
||||
//-- user_id is a property of the abstract class Importer, which this class inherits from and it's setted by
|
||||
//-- the class that needs to use it (command importer or GUI importer inside the project).
|
||||
if (isset($target)) {
|
||||
if (isset($target) && ($target !== false)) {
|
||||
if (!is_null($asset->assigned_to)){
|
||||
if ($asset->assigned_to != $target->id){
|
||||
event(new CheckoutableCheckedIn($asset, User::find($asset->assigned_to), Auth::user(), $asset->notes, date('Y-m-d H:i:s')));
|
||||
}
|
||||
}
|
||||
|
||||
$asset->fresh()->checkOut($target, $this->user_id, date('Y-m-d H:i:s'), null, $asset->notes, $asset->name);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ class ComponentImporter extends ItemImporter
|
||||
{
|
||||
$component = null;
|
||||
$this->log('Creating Component');
|
||||
$component = Component::where('name', $this->item['name'])
|
||||
->where('serial', $this->item['serial'])
|
||||
$component = Component::where('name', trim($this->item['name']))
|
||||
->where('serial', trim($this->item['serial']))
|
||||
->first();
|
||||
|
||||
if ($component) {
|
||||
|
||||
@@ -26,7 +26,7 @@ class ConsumableImporter extends ItemImporter
|
||||
*/
|
||||
public function createConsumableIfNotExists($row)
|
||||
{
|
||||
$consumable = Consumable::where('name', $this->item['name'])->first();
|
||||
$consumable = Consumable::where('name', trim($this->item['name']))->first();
|
||||
if ($consumable) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Consumable '.$this->item['name'].' already exists. ');
|
||||
@@ -41,9 +41,9 @@ class ConsumableImporter extends ItemImporter
|
||||
}
|
||||
$this->log('No matching consumable, creating one');
|
||||
$consumable = new Consumable();
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, 'model_number');
|
||||
$this->item['item_no'] = $this->findCsvMatch($row, 'item_number');
|
||||
$this->item['min_amt'] = $this->findCsvMatch($row, "min_amt");
|
||||
$this->item['model_number'] = trim($this->findCsvMatch($row, 'model_number'));
|
||||
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
||||
$this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt"));
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$consumable->unsetEventDispatcher();
|
||||
|
||||
@@ -19,22 +19,76 @@ abstract class Importer
|
||||
* Id of User performing import
|
||||
* @var
|
||||
*/
|
||||
|
||||
protected $user_id;
|
||||
/**
|
||||
* Are we updating items in the import
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
protected $updating;
|
||||
|
||||
/**
|
||||
* Default Map of item fields->csv names
|
||||
*
|
||||
* This has been moved into Livewire/Importer.php to be more granular.
|
||||
* @todo - remove references to this property since we don't use it anymore.
|
||||
* This has been moved into app/Http/Livewire/Importer.php to be more granular.
|
||||
* This private variable is ONLY used for the cli-importer.
|
||||
*
|
||||
* @todo - find a way to make this less duplicative
|
||||
* @var array
|
||||
*/
|
||||
private $defaultFieldMap = [
|
||||
|
||||
'asset_tag' => 'asset tag',
|
||||
'activated' => 'activated',
|
||||
'category' => 'category',
|
||||
'checkout_class' => 'checkout type', // Supports Location or User for assets. Using checkout_class instead of checkout_type because type exists on asset already.
|
||||
'checkout_location' => 'checkout location',
|
||||
'company' => 'company',
|
||||
'item_name' => 'item name',
|
||||
'item_number' => 'item number',
|
||||
'image' => 'image',
|
||||
'expiration_date' => 'expiration date',
|
||||
'location' => 'location',
|
||||
'notes' => 'notes',
|
||||
'license_email' => 'licensed to email',
|
||||
'license_name' => 'licensed to name',
|
||||
'maintained' => 'maintained',
|
||||
'manufacturer' => 'manufacturer',
|
||||
'asset_model' => 'model name',
|
||||
'model_number' => 'model number',
|
||||
'order_number' => 'order number',
|
||||
'purchase_cost' => 'purchase cost',
|
||||
'purchase_date' => 'purchase date',
|
||||
'purchase_order' => 'purchase order',
|
||||
'qty' => 'quantity',
|
||||
'reassignable' => 'reassignable',
|
||||
'requestable' => 'requestable',
|
||||
'seats' => 'seats',
|
||||
'serial' => 'serial number',
|
||||
'status' => 'status',
|
||||
'supplier' => 'supplier',
|
||||
'termination_date' => 'termination date',
|
||||
'warranty_months' => 'warranty',
|
||||
'full_name' => 'full name',
|
||||
'email' => 'email',
|
||||
'username' => 'username',
|
||||
'address' => 'address',
|
||||
'address2' => 'address2',
|
||||
'city' => 'city',
|
||||
'state' => 'state',
|
||||
'country' => 'country',
|
||||
'zip' => 'zip',
|
||||
'jobtitle' => 'job title',
|
||||
'employee_num' => 'employee number',
|
||||
'phone_number' => 'phone number',
|
||||
'first_name' => 'first name',
|
||||
'last_name' => 'last name',
|
||||
'department' => 'department',
|
||||
'manager_name' => 'manager full name',
|
||||
'manager_username' => 'manager username',
|
||||
'min_amt' => 'minimum quantity',
|
||||
'remote' => 'remote',
|
||||
'vip' => 'vip',
|
||||
];
|
||||
/**
|
||||
* Map of item fields->csv names
|
||||
@@ -281,9 +335,11 @@ abstract class Importer
|
||||
$user_array['email'] = User::generateEmailFromFullName($user_array['full_name']);
|
||||
}
|
||||
|
||||
// Get some variables for $user_formatted_array in case we need them later
|
||||
$user_formatted_array = User::generateFormattedNameFromFullName($user_array['full_name'], Setting::getSettings()->username_format);
|
||||
|
||||
if (empty($user_array['first_name'])) {
|
||||
// Get some fields for first name and last name based off of full name
|
||||
$user_formatted_array = User::generateFormattedNameFromFullName($user_array['full_name'], Setting::getSettings()->username_format);
|
||||
$user_array['first_name'] = $user_formatted_array['first_name'];
|
||||
$user_array['last_name'] = $user_formatted_array['last_name'];
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use App\Models\Manufacturer;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ItemImporter extends Importer
|
||||
{
|
||||
@@ -88,8 +90,14 @@ class ItemImporter extends Importer
|
||||
}
|
||||
|
||||
$this->item['asset_eol_date'] = null;
|
||||
if ($this->findCsvMatch($row, 'asset_eol_date') != '') {
|
||||
$this->item['asset_eol_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'asset_eol_date')));
|
||||
if($this->findCsvMatch($row, 'asset_eol_date') != '') {
|
||||
$csvMatch = $this->findCsvMatch($row, 'asset_eol_date');
|
||||
try {
|
||||
$this->item['asset_eol_date'] = CarbonImmutable::parse($csvMatch)->format('Y-m-d');
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e->getMessage());
|
||||
$this->log('Unable to parse date: '.$csvMatch);
|
||||
}
|
||||
}
|
||||
|
||||
$this->item['qty'] = $this->findCsvMatch($row, 'quantity');
|
||||
@@ -364,7 +372,7 @@ class ItemImporter extends Importer
|
||||
if (empty($asset_statuslabel_name)) {
|
||||
return null;
|
||||
}
|
||||
$status = Statuslabel::where(['name' => $asset_statuslabel_name])->first();
|
||||
$status = Statuslabel::where(['name' => trim($asset_statuslabel_name)])->first();
|
||||
|
||||
if ($status) {
|
||||
$this->log('A matching Status '.$asset_statuslabel_name.' already exists');
|
||||
@@ -373,7 +381,7 @@ class ItemImporter extends Importer
|
||||
}
|
||||
$this->log('Creating a new status');
|
||||
$status = new Statuslabel();
|
||||
$status->name = $asset_statuslabel_name;
|
||||
$status->name = trim($asset_statuslabel_name);
|
||||
|
||||
$status->deployable = 1;
|
||||
$status->pending = 0;
|
||||
@@ -412,7 +420,7 @@ class ItemImporter extends Importer
|
||||
|
||||
//Otherwise create a manufacturer.
|
||||
$manufacturer = new Manufacturer();
|
||||
$manufacturer->name = $item_manufacturer;
|
||||
$manufacturer->name = trim($item_manufacturer);
|
||||
$manufacturer->user_id = $this->user_id;
|
||||
|
||||
if ($manufacturer->save()) {
|
||||
|
||||
@@ -55,18 +55,19 @@ class LicenseImporter extends ItemImporter
|
||||
$this->log('No Matching License, Creating a new one');
|
||||
$license = new License;
|
||||
}
|
||||
$asset_tag = $this->item['asset_tag'] = $this->findCsvMatch($row, 'asset_tag'); // used for checkout out to an asset.
|
||||
$asset_tag = $this->item['asset_tag'] = trim($this->findCsvMatch($row, 'asset_tag')); // used for checkout out to an asset.
|
||||
|
||||
$this->item["expiration_date"] = null;
|
||||
if ($this->findCsvMatch($row, "expiration_date")!='') {
|
||||
$this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "expiration_date")));
|
||||
$this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime(trim($this->findCsvMatch($row, "expiration_date"))));
|
||||
}
|
||||
$this->item['license_email'] = $this->findCsvMatch($row, 'license_email');
|
||||
$this->item['license_name'] = $this->findCsvMatch($row, 'license_name');
|
||||
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
|
||||
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
|
||||
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
|
||||
$this->item['manufacturer'] = $this->createOrFetchManufacturer($this->findCsvMatch($row, 'manufacturer'));
|
||||
$this->item['license_email'] = trim($this->findCsvMatch($row, 'license_email'));
|
||||
$this->item['license_name'] = trim($this->findCsvMatch($row, 'license_name'));
|
||||
$this->item['maintained'] = trim($this->findCsvMatch($row, 'maintained'));
|
||||
$this->item['purchase_order'] = trim($this->findCsvMatch($row, 'purchase_order'));
|
||||
$this->item['order_number'] = trim($this->findCsvMatch($row, 'order_number'));
|
||||
$this->item['reassignable'] = trim($this->findCsvMatch($row, 'reassignable'));
|
||||
$this->item['manufacturer'] = $this->createOrFetchManufacturer(trim($this->findCsvMatch($row, 'manufacturer')));
|
||||
|
||||
if($this->item['reassignable'] == "")
|
||||
{
|
||||
|
||||
@@ -53,21 +53,21 @@ class LocationImporter extends ItemImporter
|
||||
}
|
||||
|
||||
// Pull the records from the CSV to determine their values
|
||||
$this->item['name'] = $this->findCsvMatch($row, 'name');
|
||||
$this->item['address'] = $this->findCsvMatch($row, 'address');
|
||||
$this->item['address2'] = $this->findCsvMatch($row, 'address2');
|
||||
$this->item['city'] = $this->findCsvMatch($row, 'city');
|
||||
$this->item['state'] = $this->findCsvMatch($row, 'state');
|
||||
$this->item['country'] = $this->findCsvMatch($row, 'country');
|
||||
$this->item['zip'] = $this->findCsvMatch($row, 'zip');
|
||||
$this->item['currency'] = $this->findCsvMatch($row, 'currency');
|
||||
$this->item['ldap_ou'] = $this->findCsvMatch($row, 'ldap_ou');
|
||||
$this->item['manager'] = $this->findCsvMatch($row, 'manager');
|
||||
$this->item['manager_username'] = $this->findCsvMatch($row, 'manager_username');
|
||||
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
|
||||
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));
|
||||
$this->item['address2'] = trim($this->findCsvMatch($row, 'address2'));
|
||||
$this->item['city'] = trim($this->findCsvMatch($row, 'city'));
|
||||
$this->item['state'] = trim($this->findCsvMatch($row, 'state'));
|
||||
$this->item['country'] = trim($this->findCsvMatch($row, 'country'));
|
||||
$this->item['zip'] = trim($this->findCsvMatch($row, 'zip'));
|
||||
$this->item['currency'] = trim($this->findCsvMatch($row, 'currency'));
|
||||
$this->item['ldap_ou'] = trim($this->findCsvMatch($row, 'ldap_ou'));
|
||||
$this->item['manager'] = trim($this->findCsvMatch($row, 'manager'));
|
||||
$this->item['manager_username'] = trim($this->findCsvMatch($row, 'manager_username'));
|
||||
$this->item['user_id'] = \Auth::user()->id;
|
||||
|
||||
if ($this->findCsvMatch($row, 'parent_location')) {
|
||||
$this->item['parent_id'] = $this->createOrFetchLocation($this->findCsvMatch($row, 'parent_location'));
|
||||
$this->item['parent_id'] = $this->createOrFetchLocation(trim($this->findCsvMatch($row, 'parent_location')));
|
||||
}
|
||||
|
||||
if (!empty($this->item['manager'])) {
|
||||
|
||||
@@ -42,32 +42,32 @@ class UserImporter extends ItemImporter
|
||||
public function createUserIfNotExists(array $row)
|
||||
{
|
||||
// Pull the records from the CSV to determine their values
|
||||
$this->item['id'] = $this->findCsvMatch($row, 'id');
|
||||
$this->item['username'] = $this->findCsvMatch($row, 'username');
|
||||
$this->item['first_name'] = $this->findCsvMatch($row, 'first_name');
|
||||
$this->item['last_name'] = $this->findCsvMatch($row, 'last_name');
|
||||
$this->item['email'] = $this->findCsvMatch($row, 'email');
|
||||
$this->item['gravatar'] = $this->findCsvMatch($row, 'gravatar');
|
||||
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
|
||||
$this->item['website'] = $this->findCsvMatch($row, 'website');
|
||||
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
|
||||
$this->item['address'] = $this->findCsvMatch($row, 'address');
|
||||
$this->item['city'] = $this->findCsvMatch($row, 'city');
|
||||
$this->item['state'] = $this->findCsvMatch($row, 'state');
|
||||
$this->item['country'] = $this->findCsvMatch($row, 'country');
|
||||
$this->item['start_date'] = $this->findCsvMatch($row, 'start_date');
|
||||
$this->item['end_date'] = $this->findCsvMatch($row, 'end_date');
|
||||
$this->item['zip'] = $this->findCsvMatch($row, 'zip');
|
||||
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
|
||||
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department'));
|
||||
$this->item['manager_id'] = $this->fetchManager($this->findCsvMatch($row, 'manager_first_name'), $this->findCsvMatch($row, 'manager_last_name'));
|
||||
$this->item['remote'] =($this->fetchHumanBoolean($this->findCsvMatch($row, 'remote')) ==1 ) ? '1' : 0;
|
||||
$this->item['vip'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'vip')) ==1 ) ? '1' : 0;
|
||||
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'autoassign_licenses')) ==1 ) ? '1' : 0;
|
||||
$this->item['id'] = trim($this->findCsvMatch($row, 'id'));
|
||||
$this->item['username'] = trim($this->findCsvMatch($row, 'username'));
|
||||
$this->item['first_name'] = trim($this->findCsvMatch($row, 'first_name'));
|
||||
$this->item['last_name'] = trim($this->findCsvMatch($row, 'last_name'));
|
||||
$this->item['email'] = trim($this->findCsvMatch($row, 'email'));
|
||||
$this->item['gravatar'] = trim($this->findCsvMatch($row, 'gravatar'));
|
||||
$this->item['phone'] = trim($this->findCsvMatch($row, 'phone_number'));
|
||||
$this->item['website'] = trim($this->findCsvMatch($row, 'website'));
|
||||
$this->item['jobtitle'] = trim($this->findCsvMatch($row, 'jobtitle'));
|
||||
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));
|
||||
$this->item['city'] = trim($this->findCsvMatch($row, 'city'));
|
||||
$this->item['state'] = trim($this->findCsvMatch($row, 'state'));
|
||||
$this->item['country'] = trim($this->findCsvMatch($row, 'country'));
|
||||
$this->item['start_date'] = trim($this->findCsvMatch($row, 'start_date'));
|
||||
$this->item['end_date'] = trim($this->findCsvMatch($row, 'end_date'));
|
||||
$this->item['zip'] = trim($this->findCsvMatch($row, 'zip'));
|
||||
$this->item['activated'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'activated'))) == 1) ? '1' : 0;
|
||||
$this->item['employee_num'] = trim($this->findCsvMatch($row, 'employee_num'));
|
||||
$this->item['department_id'] = trim($this->createOrFetchDepartment(trim($this->findCsvMatch($row, 'department'))));
|
||||
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
|
||||
$this->item['remote'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'remote'))) == 1 ) ? '1' : 0;
|
||||
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
|
||||
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;
|
||||
|
||||
|
||||
$user_department = $this->findCsvMatch($row, 'department');
|
||||
$user_department = trim($this->findCsvMatch($row, 'department'));
|
||||
if ($this->shouldUpdateField($user_department)) {
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($user_department);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use App\Notifications\CheckoutAccessoryNotification;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CheckoutConsumableNotification;
|
||||
use App\Notifications\CheckoutLicenseSeatNotification;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Exception;
|
||||
use Log;
|
||||
@@ -41,14 +42,9 @@ class CheckoutableListener
|
||||
/**
|
||||
* Make a checkout acceptance and attach it in the notification
|
||||
*/
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckoutNotification($event));
|
||||
}
|
||||
|
||||
if (! $event->checkedOutTo->locale) {
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
$this->getNotifiables($event),
|
||||
@@ -60,8 +56,15 @@ class CheckoutableListener
|
||||
$this->getCheckoutNotification($event, $acceptance)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckoutNotification($event));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error("Exception caught during checkout notification: ".$e->getMessage());
|
||||
Log::error("Exception caught during checkout notification: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,11 +95,6 @@ class CheckoutableListener
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
|
||||
// Use default locale
|
||||
if (! $event->checkedOutTo->locale) {
|
||||
Notification::locale(Setting::getSettings()->locale)->send(
|
||||
@@ -109,8 +107,15 @@ class CheckoutableListener
|
||||
$this->getCheckinNotification($event)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||
->notify($this->getCheckinNotification($event));
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error("Exception caught during checkin notification: ".$e->getMessage());
|
||||
Log::error("Exception caught during checkin notification: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ class LogListener
|
||||
$logaction->item()->associate($event->acceptance->checkoutable->license);
|
||||
}
|
||||
|
||||
\Log::debug('New onCheckoutAccepted Listener fired. logaction: '.print_r($logaction, true));
|
||||
$logaction->save();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Events\AssetCheckedOut;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Traits\UniqueSerialTrait;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\Searchable;
|
||||
@@ -32,7 +31,7 @@ class Asset extends Depreciable
|
||||
protected $presenter = \App\Presenters\AssetPresenter::class;
|
||||
|
||||
use CompanyableTrait;
|
||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait;
|
||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||
|
||||
public const LOCATION = 'location';
|
||||
public const ASSET = 'asset';
|
||||
@@ -72,6 +71,7 @@ class Asset extends Depreciable
|
||||
|
||||
protected $casts = [
|
||||
'purchase_date' => 'date',
|
||||
'eol_explicit' => 'boolean',
|
||||
'last_checkout' => 'datetime',
|
||||
'last_checkin' => 'datetime',
|
||||
'expected_checkin' => 'date',
|
||||
@@ -90,7 +90,7 @@ class Asset extends Depreciable
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'max:255|nullable',
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL',
|
||||
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'company_id' => 'integer|nullable',
|
||||
'warranty_months' => 'numeric|nullable|digits_between:0,240',
|
||||
@@ -99,15 +99,17 @@ class Asset extends Depreciable
|
||||
'expected_checkin' => 'date|nullable',
|
||||
'location_id' => 'exists:locations,id|nullable',
|
||||
'rtd_location_id' => 'exists:locations,id|nullable',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
|
||||
'purchase_date' => 'date|date_format:Y-m-d|nullable',
|
||||
'serial' => 'unique_serial|nullable',
|
||||
'serial' => 'unique_undeleted:assets,serial|nullable',
|
||||
'purchase_cost' => 'numeric|nullable|gte:0',
|
||||
'supplier_id' => 'exists:suppliers,id|nullable',
|
||||
'asset_eol_date' => 'date|max:10|min:10|nullable',
|
||||
'asset_eol_date' => 'date|nullable',
|
||||
'eol_explicit' => 'boolean|nullable',
|
||||
'byod' => 'boolean',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
@@ -136,8 +138,10 @@ class Asset extends Depreciable
|
||||
'expected_checkin',
|
||||
'byod',
|
||||
'asset_eol_date',
|
||||
'eol_explicit',
|
||||
'last_audit_date',
|
||||
'next_audit_date',
|
||||
'asset_eol_date',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
@@ -208,16 +212,16 @@ class Asset extends Depreciable
|
||||
|
||||
$this->rules += $model->fieldset->validation_rules();
|
||||
|
||||
foreach ($this->model->fieldset->fields as $field){
|
||||
if($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
if ($this->model->fieldset){
|
||||
foreach ($this->model->fieldset->fields as $field){
|
||||
if($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return parent::save($params);
|
||||
}
|
||||
|
||||
@@ -262,7 +266,7 @@ class Asset extends Depreciable
|
||||
|
||||
/**
|
||||
* Determines if an asset is available for checkout.
|
||||
* This checks to see if the it's checked out to an invalid (deleted) user
|
||||
* This checks to see if it's checked out to an invalid (deleted) user
|
||||
* OR if the assigned_to and deleted_at fields on the asset are empty AND
|
||||
* that the status is deployable
|
||||
*
|
||||
@@ -749,7 +753,7 @@ class Asset extends Depreciable
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> status relationship
|
||||
* Establishes the asset -> license seats relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
@@ -785,7 +789,6 @@ class Asset extends Depreciable
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the next autoincremented asset tag
|
||||
*
|
||||
@@ -918,6 +921,27 @@ class Asset extends Depreciable
|
||||
return $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* This sets the requestable to a boolean 0 or 1. This accounts for forms or API calls that
|
||||
* explicitly pass the requestable field but it has a null or empty value.
|
||||
*
|
||||
* This will also correctly parse a 1/0 if "true"/"false" is passed.
|
||||
*
|
||||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRequestableAttribute($value)
|
||||
{
|
||||
$this->attributes['requestable'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
@@ -948,6 +972,7 @@ class Asset extends Depreciable
|
||||
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
|
||||
->orWhereMultipleColumns([
|
||||
'assets_users.first_name',
|
||||
'assets_users.last_name',
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
@@ -188,6 +189,21 @@ class AssetModel extends SnipeModel
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the model is deletable
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v6.3.4]
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assets_count == 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uploads for this model
|
||||
*
|
||||
@@ -291,4 +307,9 @@ class AssetModel extends SnipeModel
|
||||
{
|
||||
return $query->leftJoin('categories', 'models.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
|
||||
}
|
||||
|
||||
public function scopeOrderFieldset($query, $order)
|
||||
{
|
||||
return $query->leftJoin('custom_fieldsets', 'models.fieldset_id', '=', 'custom_fieldsets.id')->orderBy('custom_fieldsets.name', $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ class Category extends SnipeModel
|
||||
{
|
||||
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->itemCount() == 0);
|
||||
&& ($this->itemCount() == 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +248,26 @@ class Category extends SnipeModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* This sets the checkin_value to a boolean 0 or 1. This accounts for forms or API calls that
|
||||
* explicitly pass the checkin_email field but it has a null or empty value.
|
||||
*
|
||||
* This will also correctly parse a 1/0 if "true"/"false" is passed.
|
||||
*
|
||||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function setCheckinEmailAttribute($value)
|
||||
{
|
||||
$this->attributes['checkin_email'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
|
||||
@@ -53,6 +53,12 @@ class CustomField extends Model
|
||||
'field_encrypted' => 'nullable|boolean',
|
||||
'auto_add_to_fieldsets' => 'boolean',
|
||||
'show_in_listview' => 'boolean',
|
||||
'show_in_requestable_list' => 'boolean',
|
||||
'show_in_email' => 'boolean',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'show_in_requestable_list' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -72,7 +78,8 @@ class CustomField extends Model
|
||||
'display_in_user_view',
|
||||
'auto_add_to_fieldsets',
|
||||
'show_in_listview',
|
||||
|
||||
'show_in_email',
|
||||
'show_in_requestable_list',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -243,8 +250,6 @@ class CustomField extends Model
|
||||
/**
|
||||
* Gets the DB column name.
|
||||
*
|
||||
* @todo figure out if this is still needed? I don't know WTF it's for.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return string
|
||||
|
||||
@@ -92,6 +92,8 @@ class CustomFieldset extends Model
|
||||
|
||||
array_push($rule, $field->attributes['format']);
|
||||
$rules[$field->db_column_name()] = $rule;
|
||||
//add not_array to rules for all fields
|
||||
$rules[$field->db_column_name()][] = 'not_array';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
||||
@@ -21,6 +21,8 @@ class Field {
|
||||
|
||||
public static function makeArray(Field $field, Asset $asset) {
|
||||
return $field->getOptions()
|
||||
// filter out any FieldOptions that are accidentally null
|
||||
->filter()
|
||||
->map(fn($option) => $option->toArray($asset))
|
||||
->filter(fn($result) => $result['value'] != null);
|
||||
}
|
||||
@@ -36,4 +38,4 @@ class Field {
|
||||
->map(fn($optionString) => FieldOption::fromString($optionString));
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ class FieldOption {
|
||||
|
||||
public function getValue(Asset $asset) {
|
||||
$dataPath = collect(explode('.', $this->dataSource));
|
||||
|
||||
// assignedTo directly on the asset is a special case where
|
||||
// we want to avoid returning the property directly
|
||||
// and instead return the entity's presented name.
|
||||
if ($dataPath[0] === 'assignedTo'){
|
||||
return $asset->assignedTo ? $asset->assignedTo->present()->fullName() : null;
|
||||
}
|
||||
|
||||
return $dataPath->reduce(function ($myValue, $path) {
|
||||
try { return $myValue ? $myValue->{$path} : ${$myValue}; }
|
||||
catch (\Exception $e) { return $myValue; }
|
||||
@@ -46,4 +54,4 @@ class FieldOption {
|
||||
return $option;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use TCPDF;
|
||||
use TCPDF_STATIC;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Model for Labels.
|
||||
@@ -372,8 +373,8 @@ abstract class Label
|
||||
if (empty($value)) return;
|
||||
try {
|
||||
$pdf->write1DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('The 1D barcode ' . $value . ' is not compliant with the barcode type '. $type);
|
||||
} catch (\Exception|TypeError $e) {
|
||||
\Log::debug('The 1D barcode ' . $value . ' is not compliant with the barcode type '. $type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class License extends Depreciable
|
||||
protected $guarded = 'id';
|
||||
protected $table = 'licenses';
|
||||
|
||||
|
||||
protected $casts = [
|
||||
'purchase_date' => 'date',
|
||||
'expiration_date' => 'date',
|
||||
|
||||
@@ -77,7 +77,8 @@ class Manufacturer extends SnipeModel
|
||||
&& ($this->assets()->count() === 0)
|
||||
&& ($this->licenses()->count() === 0)
|
||||
&& ($this->consumables()->count() === 0)
|
||||
&& ($this->accessories()->count() === 0);
|
||||
&& ($this->accessories()->count() === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
public function assets()
|
||||
|
||||
@@ -92,6 +92,10 @@ class Setting extends Model
|
||||
'google_client_secret',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'label2_asset_logo' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the app settings.
|
||||
* Cache is expired on Setting model saved in EventServiceProvider.
|
||||
|
||||
@@ -129,8 +129,20 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig
|
||||
'preferredLanguage' => AttributeMapping::eloquent('locale'), // Section 5.3.5 of [RFC7231]
|
||||
'locale' => null, // see RFC5646
|
||||
'timezone' => null, // see RFC6557
|
||||
'active' => AttributeMapping::eloquent('activated'),
|
||||
|
||||
'active' => (new AttributeMapping())->setAdd(
|
||||
function ($value, &$object) {
|
||||
$object->activated = $value;
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
$object->activated = $value;
|
||||
}
|
||||
)->setRead(
|
||||
// this works as specified.
|
||||
function (&$object) {
|
||||
return (bool)$object->activated;
|
||||
}
|
||||
),
|
||||
'password' => AttributeMapping::eloquent('password')->disableRead(),
|
||||
|
||||
// Multi-Valued Attributes
|
||||
|
||||
@@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
@@ -201,6 +202,23 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
return $this->checkPermissionSection('superuser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user is deletable
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v6.3.4]
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assets()->count() === 0)
|
||||
&& ($this->licenses()->count() === 0)
|
||||
&& ($this->consumables()->count() === 0)
|
||||
&& ($this->accessories()->count() === 0)
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the user -> company relationship
|
||||
|
||||
@@ -26,7 +26,6 @@ class CheckinAccessoryNotification extends Notification
|
||||
$this->admin = $checkedInby;
|
||||
$this->note = $note;
|
||||
$this->settings = Setting::getSettings();
|
||||
\Log::debug('Constructor for notification fired');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
||||
@@ -6,11 +6,12 @@ use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class AssetObserver
|
||||
{
|
||||
/**
|
||||
* Listen to the User created event.
|
||||
* Listen to the Asset updating event. This fires automatically every time an existing asset is saved.
|
||||
*
|
||||
* @param Asset $asset
|
||||
* @return void
|
||||
@@ -21,6 +22,13 @@ class AssetObserver
|
||||
$attributesOriginal = $asset->getRawOriginal();
|
||||
$same_checkout_counter = false;
|
||||
$same_checkin_counter = false;
|
||||
$restoring_or_deleting = false;
|
||||
|
||||
|
||||
// This is a gross hack to prevent the double logging when restoring an asset
|
||||
if (array_key_exists('deleted_at', $attributes) && array_key_exists('deleted_at', $attributesOriginal)){
|
||||
$restoring_or_deleting = (($attributes['deleted_at'] != $attributesOriginal['deleted_at']));
|
||||
}
|
||||
|
||||
if (array_key_exists('checkout_counter', $attributes) && array_key_exists('checkout_counter', $attributesOriginal)){
|
||||
$same_checkout_counter = (($attributes['checkout_counter'] == $attributesOriginal['checkout_counter']));
|
||||
@@ -32,10 +40,10 @@ class AssetObserver
|
||||
|
||||
// If the asset isn't being checked out or audited, log the update.
|
||||
// (Those other actions already create log entries.)
|
||||
if (($attributes['assigned_to'] == $attributesOriginal['assigned_to'])
|
||||
if (($attributes['assigned_to'] == $attributesOriginal['assigned_to'])
|
||||
&& ($same_checkout_counter) && ($same_checkin_counter)
|
||||
&& ((isset( $attributes['next_audit_date']) ? $attributes['next_audit_date'] : null) == (isset($attributesOriginal['next_audit_date']) ? $attributesOriginal['next_audit_date']: null))
|
||||
&& ($attributes['last_checkout'] == $attributesOriginal['last_checkout']))
|
||||
&& ($attributes['last_checkout'] == $attributesOriginal['last_checkout']) && (!$restoring_or_deleting))
|
||||
{
|
||||
$changed = [];
|
||||
|
||||
@@ -119,4 +127,58 @@ class AssetObserver
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to the Asset deleting event.
|
||||
*
|
||||
* @param Asset $asset
|
||||
* @return void
|
||||
*/
|
||||
public function restoring(Asset $asset)
|
||||
{
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('restore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes every time an asset is saved.
|
||||
*
|
||||
* This matters specifically because any database fields affected here MUST already exist on
|
||||
* the assets table (and/or any related models), or related migrations WILL fail.
|
||||
*
|
||||
* For example, if there is a database migration that's a bit older and modifies an asset, if the save
|
||||
* fires before a field gets created in a later migration and that field in the later migration
|
||||
* is used in this observer, it doesn't actually exist yet and the migration will break unless we
|
||||
* use saveQuietly() in the migration which skips this observer.
|
||||
*
|
||||
* @see https://github.com/snipe/snipe-it/issues/13723#issuecomment-1761315938
|
||||
*/
|
||||
public function saving(Asset $asset)
|
||||
{
|
||||
// determine if calculated eol and then calculate it - this should only happen on a new asset
|
||||
if (is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && ($asset->model->eol > 0)){
|
||||
$asset->asset_eol_date = $asset->purchase_date->addMonths($asset->model->eol)->format('Y-m-d');
|
||||
$asset->eol_explicit = false;
|
||||
}
|
||||
|
||||
// determine if explicit and set eol_explicit to true
|
||||
if (!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) {
|
||||
if($asset->model->eol > 0) {
|
||||
$months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
if($months != $asset->model->eol) {
|
||||
$asset->eol_explicit = true;
|
||||
}
|
||||
}
|
||||
} elseif (!is_null($asset->asset_eol_date) && is_null($asset->purchase_date)) {
|
||||
$asset->eol_explicit = true;
|
||||
}
|
||||
if ((!is_null($asset->asset_eol_date)) && (!is_null($asset->purchase_date)) && (is_null($asset->model->eol) || ($asset->model->eol == 0))) {
|
||||
$asset->eol_explicit = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
149
app/Observers/UserObserver.php
Normal file
149
app/Observers/UserObserver.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
|
||||
class UserObserver
|
||||
{
|
||||
/**
|
||||
* Listen to the User updating event. This fires automatically every time an existing asset is saved.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function updating(User $user)
|
||||
{
|
||||
|
||||
// ONLY allow these fields to be stored
|
||||
$allowed_fields = [
|
||||
'email',
|
||||
'activated',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'website',
|
||||
'country',
|
||||
'gravatar',
|
||||
'location_id',
|
||||
'phone',
|
||||
'jobtitle',
|
||||
'manager_id',
|
||||
'employee_num',
|
||||
'username',
|
||||
'notes',
|
||||
'company_id',
|
||||
'ldap_import',
|
||||
'locale',
|
||||
'two_factor_enrolled',
|
||||
'two_factor_optin',
|
||||
'department_id',
|
||||
'address',
|
||||
'address2',
|
||||
'city',
|
||||
'state',
|
||||
'zip',
|
||||
'remote',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'autoassign_licenses',
|
||||
'vip',
|
||||
'password'
|
||||
];
|
||||
|
||||
$changed = [];
|
||||
|
||||
foreach ($user->getRawOriginal() as $key => $value) {
|
||||
|
||||
// Make sure the info is in the allow fields array
|
||||
if (in_array($key, $allowed_fields)) {
|
||||
|
||||
// Check and see if the value changed
|
||||
if ($user->getRawOriginal()[$key] != $user->getAttributes()[$key]) {
|
||||
|
||||
$changed[$key]['old'] = $user->getRawOriginal()[$key];
|
||||
$changed[$key]['new'] = $user->getAttributes()[$key];
|
||||
|
||||
// Do not store the hashed password in changes
|
||||
if ($key == 'password') {
|
||||
$changed['password']['old'] = '*************';
|
||||
$changed['password']['new'] = '*************';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (count($changed) > 0) {
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
|
||||
$logAction->target_id = $user->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
$logAction->logaction('update');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to the User created event, and increment
|
||||
* the next_auto_tag_base value in the settings table when i
|
||||
* a new asset is created.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function created(User $user)
|
||||
{
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = User::class; // can we instead say $logAction->item = $asset ?
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to the User deleting event.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function deleting(User $user)
|
||||
{
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
|
||||
$logAction->target_id = $user->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to the User deleting event.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function restoring(User $user)
|
||||
{
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = User::class;
|
||||
$logAction->item_id = $user->id;
|
||||
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
|
||||
$logAction->target_id = $user->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('restore');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -38,22 +38,63 @@ class ActionlogPresenter extends Presenter
|
||||
|
||||
public function icon()
|
||||
{
|
||||
$itemicon = 'fas fa-paperclip';
|
||||
|
||||
// User related icons
|
||||
if ($this->itemType() == 'user') {
|
||||
|
||||
if ($this->itemType() == 'asset') {
|
||||
return 'fas fa-barcode';
|
||||
} elseif ($this->itemType() == 'accessory') {
|
||||
return 'far fa-keyboard';
|
||||
} elseif ($this->itemType() == 'consumable') {
|
||||
return 'fas fa-tint';
|
||||
} elseif ($this->itemType() == 'license') {
|
||||
return 'far fa-save';
|
||||
} elseif ($this->itemType() == 'component') {
|
||||
return 'far fa-hdd';
|
||||
} elseif ($this->itemType() == 'user') {
|
||||
return 'fa-solid fa-people-arrows';
|
||||
if ($this->actionType()=='create new') {
|
||||
return 'fa-solid fa-user-plus';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='merged') {
|
||||
return 'fa-solid fa-people-arrows';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='delete') {
|
||||
return 'fa-solid fa-user-minus';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='delete') {
|
||||
return 'fa-solid fa-user-minus';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='update') {
|
||||
return 'fa-solid fa-user-pen';
|
||||
}
|
||||
return 'fa-solid fa-user';
|
||||
}
|
||||
|
||||
// Everything else
|
||||
if ($this->actionType()=='create new') {
|
||||
return 'fa-solid fa-plus';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='delete') {
|
||||
return 'fa-solid fa-user-xmark';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='update') {
|
||||
return 'fa-solid fa-pen';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='restore') {
|
||||
return 'fa-solid fa-trash-arrow-up';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='upload') {
|
||||
return 'fas fa-paperclip';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='checkout') {
|
||||
return 'fa-solid fa-rotate-left';
|
||||
}
|
||||
|
||||
if ($this->actionType()=='checkin from') {
|
||||
return 'fa-solid fa-rotate-right';
|
||||
}
|
||||
|
||||
return 'fa-solid fa-rotate-right';
|
||||
|
||||
}
|
||||
|
||||
public function actionType()
|
||||
|
||||
@@ -104,7 +104,7 @@ class AssetModelPresenter extends Presenter
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.eol'),
|
||||
'title' => trans('admin/hardware/form.eol_rate'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Presenters;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use Carbon\CarbonImmutable;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
@@ -172,7 +173,7 @@ class AssetPresenter extends Presenter
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.eol'),
|
||||
'title' => trans('admin/hardware/form.eol_rate'),
|
||||
],
|
||||
[
|
||||
'field' => 'asset_eol_date',
|
||||
@@ -429,10 +430,7 @@ class AssetPresenter extends Presenter
|
||||
public function eol_date()
|
||||
{
|
||||
if (($this->purchase_date) && ($this->model->model) && ($this->model->model->eol)) {
|
||||
$date = date_create($this->purchase_date);
|
||||
date_add($date, date_interval_create_from_date_string($this->model->model->eol.' months'));
|
||||
|
||||
return date_format($date, 'Y-m-d');
|
||||
return CarbonImmutable::parse($this->purchase_date)->addMonths($this->model->model->eol)->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,14 @@ class LicensePresenter extends Presenter
|
||||
'visible' => true,
|
||||
'formatter' => 'locationsLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'notes',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'visible' => false,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter'
|
||||
],
|
||||
[
|
||||
'field' => 'checkincheckout',
|
||||
'searchable' => false,
|
||||
|
||||
@@ -106,7 +106,7 @@ class LocationPresenter extends Presenter
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/locations/table.address'),
|
||||
'title' => trans('admin/locations/table.address2'),
|
||||
'visible' => false,
|
||||
],
|
||||
[
|
||||
|
||||
@@ -7,10 +7,12 @@ use App\Models\Asset;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
use App\Models\SnipeSCIMConfig;
|
||||
use App\Observers\AccessoryObserver;
|
||||
use App\Observers\AssetObserver;
|
||||
use App\Observers\UserObserver;
|
||||
use App\Observers\ComponentObserver;
|
||||
use App\Observers\ConsumableObserver;
|
||||
use App\Observers\LicenseObserver;
|
||||
@@ -58,6 +60,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
Schema::defaultStringLength(191);
|
||||
Asset::observe(AssetObserver::class);
|
||||
User::observe(UserObserver::class);
|
||||
Accessory::observe(AccessoryObserver::class);
|
||||
Component::observe(ComponentObserver::class);
|
||||
Consumable::observe(ConsumableObserver::class);
|
||||
|
||||
@@ -33,18 +33,33 @@ class SettingsServiceProvider extends ServiceProvider
|
||||
// Make sure the limit is actually set, is an integer and does not exceed system limits
|
||||
\App::singleton('api_limit_value', function () {
|
||||
$limit = config('app.max_results');
|
||||
$int_limit = intval(request('limit'));
|
||||
|
||||
if ((abs(intval(request('limit'))) > 0) && (abs(request('limit')) <= config('app.max_results'))) {
|
||||
$limit = abs(request('limit'));
|
||||
if ((abs($int_limit) > 0) && ($int_limit <= config('app.max_results'))) {
|
||||
$limit = abs($int_limit);
|
||||
}
|
||||
\Log::debug('Max in env: '.config('app.max_results'));
|
||||
\Log::debug('Original requested limit: '.request('limit'));
|
||||
\Log::debug('Modified limit: '.$limit);
|
||||
\Log::debug('------------------------------');
|
||||
|
||||
// \Log::debug('Max in env: '.config('app.max_results'));
|
||||
// \Log::debug('Original requested limit: '.request('limit'));
|
||||
// \Log::debug('Int limit: '.$int_limit);
|
||||
// \Log::debug('Modified limit: '.$limit);
|
||||
// \Log::debug('------------------------------');
|
||||
|
||||
|
||||
return $limit;
|
||||
});
|
||||
|
||||
// Make sure the offset is actually set and is an integer
|
||||
\App::singleton('api_offset_value', function () {
|
||||
$offset = intval(request('offset'));
|
||||
// \Log::debug('Original requested offset: '.request('offset'));
|
||||
// \Log::debug('Modified offset: '.$offset);
|
||||
// \Log::debug('------------------------------');
|
||||
|
||||
|
||||
return $offset;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Set some common variables so that they're globally available.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\Setting;
|
||||
use DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rule;
|
||||
@@ -45,30 +46,87 @@ class ValidationServiceProvider extends ServiceProvider
|
||||
return $validator->passes();
|
||||
});
|
||||
|
||||
// Unique only if undeleted
|
||||
// This works around the use case where multiple deleted items have the same unique attribute.
|
||||
// (I think this is a bug in Laravel's validator?)
|
||||
|
||||
/**
|
||||
* Unique only if undeleted.
|
||||
*
|
||||
* This works around the use case where multiple deleted items have the same unique attribute.
|
||||
* (I think this is a bug in Laravel's validator?)
|
||||
*
|
||||
* $attribute is the FIELDNAME you're checking against
|
||||
* $value is the VALUE of the item you're checking against the existing values in the fieldname
|
||||
* $parameters[0] is the TABLE NAME you're querying
|
||||
* $parameters[1] is the ID of the item you're querying - this makes it work on saving, checkout, etc,
|
||||
* since it defaults to 0 if there is no item created yet (new item), but populates the ID if editing
|
||||
*
|
||||
* The UniqueUndeletedTrait prefills these parameters, so you can just use
|
||||
* `unique_undeleted:table,fieldname` in your rules out of the box
|
||||
*/
|
||||
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
|
||||
if (count($parameters)) {
|
||||
$count = DB::table($parameters[0])->select('id')->where($attribute, '=', $value)->whereNull('deleted_at')->where('id', '!=', $parameters[1])->count();
|
||||
|
||||
// This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable
|
||||
if (($parameters[0]=='assets') && ($attribute == 'serial') && (Setting::getSettings()->unique_serial != '1')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$count = DB::table($parameters[0])
|
||||
->select('id')
|
||||
->where($attribute, '=', $value)
|
||||
->whereNull('deleted_at')
|
||||
->where('id', '!=', $parameters[1])->count();
|
||||
|
||||
return $count < 1;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Unique if undeleted for two columns
|
||||
*
|
||||
* Same as unique_undeleted but taking the combination of two columns as unique constrain.
|
||||
* This uses the Validator::replacer('two_column_unique_undeleted') below for nicer translations.
|
||||
*
|
||||
* $parameters[0] - the name of the first table we're looking at
|
||||
* $parameters[1] - the ID (this will be 0 on new creations)
|
||||
* $parameters[2] - the name of the second table we're looking at
|
||||
* $parameters[3] - the value that the request is passing for the second table we're
|
||||
* checking for uniqueness across
|
||||
*
|
||||
*/
|
||||
Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
if (count($parameters)) {
|
||||
$count = DB::table($parameters[0])
|
||||
->select('id')->where($attribute, '=', $value)
|
||||
->whereNull('deleted_at')
|
||||
->where('id', '!=', $parameters[1])
|
||||
->where($parameters[2], $parameters[3])->count();
|
||||
|
||||
return $count < 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Unique if undeleted for two columns
|
||||
// Same as unique_undeleted but taking the combination of two columns as unique constrain.
|
||||
Validator::extend('two_column_unique_undeleted', function ($attribute, $value, $parameters, $validator) {
|
||||
if (count($parameters)) {
|
||||
$count = DB::table($parameters[0])
|
||||
->select('id')->where($attribute, '=', $value)
|
||||
->whereNull('deleted_at')
|
||||
->where('id', '!=', $parameters[1])
|
||||
->where($parameters[2], $parameters[3])->count();
|
||||
|
||||
return $count < 1;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* This is the validator replace static method that allows us to pass the $parameters of the table names
|
||||
* into the translation string in validation.two_column_unique_undeleted for two_column_unique_undeleted
|
||||
* validation messages.
|
||||
*
|
||||
* This is invoked automatically by Validator::extend('two_column_unique_undeleted') above and
|
||||
* produces a translation like: "The name value must be unique across categories and category type."
|
||||
*
|
||||
* The $parameters passed coincide with the ones the two_column_unique_undeleted custom validator above
|
||||
* uses, so $parameter[0] is the first table and so $parameter[2] is the second table.
|
||||
*/
|
||||
Validator::replacer('two_column_unique_undeleted', function($message, $attribute, $rule, $parameters) {
|
||||
$message = str_replace(':table1', $parameters[0], $message);
|
||||
$message = str_replace(':table2', $parameters[2], $message);
|
||||
|
||||
// Change underscores to spaces for a friendlier display
|
||||
$message = str_replace('_', ' ', $message);
|
||||
return $message;
|
||||
});
|
||||
|
||||
|
||||
// Prevent circular references
|
||||
//
|
||||
@@ -232,6 +290,10 @@ class ValidationServiceProvider extends ServiceProvider
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Validator::extend('not_array', function ($attribute, $value, $parameters, $validator) {
|
||||
return !is_array($value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,19 +103,12 @@ class Label implements View
|
||||
|
||||
$logo = null;
|
||||
|
||||
// Should we be trying to use a logo at all?
|
||||
if ($settings->label2_asset_logo='1') {
|
||||
|
||||
// If we don't have a company image, fall back to the general site label image
|
||||
if (!empty($settings->label_logo)) {
|
||||
$logo = Storage::disk('public')->path('/'.e($settings->label_logo));
|
||||
}
|
||||
|
||||
// If we have a company logo, use that first
|
||||
if (($asset->company) && ($asset->company->image!='')) {
|
||||
$logo = Storage::disk('public')->path('companies/'.e($asset->company->image));
|
||||
}
|
||||
|
||||
// Should we use the assets assigned company logo? (A.K.A. "Is `Labels > Use Asset Logo` enabled?"), and do we have a company logo?
|
||||
if ($settings->label2_asset_logo && $asset->company && $asset->company->image!='') {
|
||||
$logo = Storage::disk('public')->path('companies/'.e($asset->company->image));
|
||||
} elseif (!empty($settings->label_logo)) {
|
||||
// Use the general site label logo, if available
|
||||
$logo = Storage::disk('public')->path('/'.e($settings->label_logo));
|
||||
}
|
||||
|
||||
if (!empty($logo)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user