Compare commits
342 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9035707bd6 | ||
|
|
aa1e06f021 | ||
|
|
30b1cfabf5 | ||
|
|
e75d22ab73 | ||
|
|
b1e17743b8 | ||
|
|
e2c0f01a10 | ||
|
|
f88fee0f21 | ||
|
|
c0669150fb | ||
|
|
f3c12f38b6 | ||
|
|
5e19178a30 | ||
|
|
90cddb7aee | ||
|
|
6d828964be | ||
|
|
971fcf5800 | ||
|
|
2ad270cf33 | ||
|
|
8ce78c6b31 | ||
|
|
af3c8195af | ||
|
|
117b4c59cc | ||
|
|
194d0733d4 | ||
|
|
a371e8d53f | ||
|
|
8f09cca043 | ||
|
|
39bca49e8f | ||
|
|
b8269020ae | ||
|
|
601c129bbf | ||
|
|
b293d00699 | ||
|
|
75a0cf97e2 | ||
|
|
c055e3af21 | ||
|
|
a1f93e733c | ||
|
|
49073742b5 | ||
|
|
187206cb88 | ||
|
|
8420cb7ec1 | ||
|
|
75252bce05 | ||
|
|
794824713e | ||
|
|
8f6ea84fca | ||
|
|
ea1b792a93 | ||
|
|
4ffb8f14b8 | ||
|
|
d023f61bc4 | ||
|
|
dd5ca73602 | ||
|
|
2632f730d1 | ||
|
|
24c158bfe6 | ||
|
|
3d4a5a8066 | ||
|
|
db7e0b56f2 | ||
|
|
f2478d813c | ||
|
|
192aa9eb71 | ||
|
|
0eef0fc1dd | ||
|
|
81f8fe34cd | ||
|
|
f744696043 | ||
|
|
29b0780c6c | ||
|
|
6b3b673daa | ||
|
|
a4876e9f3e | ||
|
|
925258bfb4 | ||
|
|
1a10aa0dda | ||
|
|
d6f8d1b464 | ||
|
|
09a102fea8 | ||
|
|
304fce73fc | ||
|
|
295a68bb7a | ||
|
|
3aeb521782 | ||
|
|
f587d2248b | ||
|
|
8579c5a68a | ||
|
|
835b461d7d | ||
|
|
b8a37a0c73 | ||
|
|
41b5b1dfd0 | ||
|
|
c7596e7741 | ||
|
|
d4fa81301d | ||
|
|
ec7245965f | ||
|
|
de76e8db5f | ||
|
|
a52575c7bf | ||
|
|
bf6703c2e8 | ||
|
|
4db1dd8afc | ||
|
|
c39e3acb59 | ||
|
|
7a44da85a0 | ||
|
|
890b613f71 | ||
|
|
1014bd74e0 | ||
|
|
db385e024b | ||
|
|
c8bff3ef38 | ||
|
|
10bc35d604 | ||
|
|
eea65a3f26 | ||
|
|
3b21a19491 | ||
|
|
7a52477294 | ||
|
|
ef0bd72076 | ||
|
|
ff824ec4db | ||
|
|
75032def9e | ||
|
|
3a0f738fb0 | ||
|
|
55846cc717 | ||
|
|
1784278a59 | ||
|
|
afac0bc441 | ||
|
|
ffbee77f6f | ||
|
|
b69b5fdf84 | ||
|
|
89e06054bf | ||
|
|
3159e7713a | ||
|
|
adf6e7d1cd | ||
|
|
ba3662a9ed | ||
|
|
05ea61421f | ||
|
|
22ef2ce0b6 | ||
|
|
51d3d130e4 | ||
|
|
d8a8e1cc09 | ||
|
|
522dc1db2a | ||
|
|
db907815ff | ||
|
|
ae6abdddad | ||
|
|
63c9fbe10c | ||
|
|
101dfd01f2 | ||
|
|
5db5134ae0 | ||
|
|
5294489b0e | ||
|
|
05b2b8fb59 | ||
|
|
0100c56046 | ||
|
|
e81b221fd1 | ||
|
|
f374ac1bf7 | ||
|
|
524c6c502e | ||
|
|
614e858e44 | ||
|
|
708b1a962c | ||
|
|
4e55a18a60 | ||
|
|
3de1de9dc6 | ||
|
|
e320d2ba05 | ||
|
|
ed78a4b8a0 | ||
|
|
376eb52f00 | ||
|
|
8ecceeacda | ||
|
|
f4cfb31bf4 | ||
|
|
227dc7e81d | ||
|
|
152d985ebc | ||
|
|
ef1e8df001 | ||
|
|
5c2b1a3b70 | ||
|
|
66c3f5432d | ||
|
|
de413408f5 | ||
|
|
059126f642 | ||
|
|
3bc43210ab | ||
|
|
82194cef8a | ||
|
|
1956a16d1e | ||
|
|
e1c095adca | ||
|
|
45a2932f4b | ||
|
|
b6e3715cd8 | ||
|
|
aa9c0078a1 | ||
|
|
9677115055 | ||
|
|
d45e90e358 | ||
|
|
f692fb5bff | ||
|
|
83692696ba | ||
|
|
bbb15d610f | ||
|
|
7ebb7876c4 | ||
|
|
bd581d01a3 | ||
|
|
de2ebba577 | ||
|
|
351274c633 | ||
|
|
53fab3e9ba | ||
|
|
a0c0b7b1eb | ||
|
|
5a34d43a86 | ||
|
|
86a0f77e6f | ||
|
|
9d00ae6e50 | ||
|
|
8d23398176 | ||
|
|
5aae930e8c | ||
|
|
6db096c336 | ||
|
|
78d27e82c8 | ||
|
|
2a4fef6a61 | ||
|
|
9daeeeb851 | ||
|
|
92671823d8 | ||
|
|
19396b2107 | ||
|
|
f0332a7388 | ||
|
|
70cb5ed215 | ||
|
|
d309f67df0 | ||
|
|
7dc070ec2c | ||
|
|
2ecdf19569 | ||
|
|
c56f2625b7 | ||
|
|
3dc154991a | ||
|
|
a5f57c050f | ||
|
|
36c4cd98e0 | ||
|
|
f6ef6039f4 | ||
|
|
6867563e4a | ||
|
|
21820262ce | ||
|
|
bae1203b64 | ||
|
|
0f2ff7aba2 | ||
|
|
0ce90834f6 | ||
|
|
0e182bb2b9 | ||
|
|
7afff69fab | ||
|
|
136d59e4b9 | ||
|
|
6d9cbac928 | ||
|
|
efcd8d339b | ||
|
|
7e09ce468b | ||
|
|
baf714b41f | ||
|
|
b0808e846d | ||
|
|
0e3e10a707 | ||
|
|
cbf3d5d071 | ||
|
|
68d355a33d | ||
|
|
853923013a | ||
|
|
737061a983 | ||
|
|
0438d55284 | ||
|
|
924962c156 | ||
|
|
a8c42843f6 | ||
|
|
63c4b7b6b6 | ||
|
|
ff06387d90 | ||
|
|
fa46622bad | ||
|
|
fd65780287 | ||
|
|
6478ec86ac | ||
|
|
748e0eb44f | ||
|
|
507c07e9eb | ||
|
|
5185a9e590 | ||
|
|
1ade088a8f | ||
|
|
5c087181be | ||
|
|
08bb314bbc | ||
|
|
7ddb9becd4 | ||
|
|
7d96709ee2 | ||
|
|
4413a9f0b6 | ||
|
|
9611b55fb9 | ||
|
|
dffd535f68 | ||
|
|
4aaf2da651 | ||
|
|
80ac46af78 | ||
|
|
13c58b54fa | ||
|
|
355119187a | ||
|
|
0d96587b04 | ||
|
|
be806def91 | ||
|
|
8acf8027e9 | ||
|
|
893a1fec57 | ||
|
|
d182e40aef | ||
|
|
6118aca949 | ||
|
|
f0ac83179f | ||
|
|
8969ffc14b | ||
|
|
9fb130146a | ||
|
|
d285ff673a | ||
|
|
7f586da856 | ||
|
|
c35f5b3116 | ||
|
|
12927d2d54 | ||
|
|
6b06b87547 | ||
|
|
058f8ae3b8 | ||
|
|
1faedac04b | ||
|
|
666c7321af | ||
|
|
6cbaf7396a | ||
|
|
b706acaa9f | ||
|
|
5870acb193 | ||
|
|
c00b633312 | ||
|
|
1c0ee7c4c5 | ||
|
|
961741b809 | ||
|
|
5a66f0f373 | ||
|
|
5a1e1c73c9 | ||
|
|
3be68ec721 | ||
|
|
0f81e494ee | ||
|
|
d88f66b019 | ||
|
|
027edbdb21 | ||
|
|
cf03d25934 | ||
|
|
75232d2a70 | ||
|
|
bcb966af12 | ||
|
|
0272e58868 | ||
|
|
bcd988bb81 | ||
|
|
aa6c21f38d | ||
|
|
3fb5f6f5be | ||
|
|
5dc2ac9e22 | ||
|
|
0f85d6810b | ||
|
|
bf761946da | ||
|
|
d9fa2f0e91 | ||
|
|
8a25677a8d | ||
|
|
7c2da81700 | ||
|
|
a4799a495a | ||
|
|
b5de5ac19c | ||
|
|
638a7b2d91 | ||
|
|
dcf72ce2da | ||
|
|
8f6e0ad5be | ||
|
|
50e0b9b84e | ||
|
|
b6b93550fe | ||
|
|
baa3be728d | ||
|
|
240e642fe9 | ||
|
|
07a92d20d7 | ||
|
|
3f334406d1 | ||
|
|
dbd177576e | ||
|
|
0fb9f42ba4 | ||
|
|
b4542d4d42 | ||
|
|
4bfd7a7e4e | ||
|
|
3fe1562b92 | ||
|
|
27699aa99c | ||
|
|
48bbbe0f40 | ||
|
|
9dc226e3d6 | ||
|
|
98b20fc1cd | ||
|
|
980dccf31c | ||
|
|
bb2193d481 | ||
|
|
bf8fe316df | ||
|
|
96716626c6 | ||
|
|
e20338ff9e | ||
|
|
78530ae123 | ||
|
|
52d605d13e | ||
|
|
0182615e7e | ||
|
|
fad84d4437 | ||
|
|
c162e9a4de | ||
|
|
bf1e742df6 | ||
|
|
0e88a6b268 | ||
|
|
c1e870528e | ||
|
|
35fc001c58 | ||
|
|
339263a295 | ||
|
|
a44bd9abe0 | ||
|
|
b850d47282 | ||
|
|
4099c06b27 | ||
|
|
e559879f91 | ||
|
|
abb95e7872 | ||
|
|
869de3d251 | ||
|
|
f3526eccb9 | ||
|
|
880faa83a6 | ||
|
|
311f9fcefb | ||
|
|
8732f299e6 | ||
|
|
b30aac536a | ||
|
|
d7dc4ae0c0 | ||
|
|
5bb4c85ccb | ||
|
|
80dda198c5 | ||
|
|
9442736518 | ||
|
|
2fbad52c71 | ||
|
|
5975c9fac7 | ||
|
|
7b0e392ecd | ||
|
|
b51a10b46b | ||
|
|
6c58f59d72 | ||
|
|
cd9caa24ad | ||
|
|
cbb4b4d846 | ||
|
|
ea4cdadc6e | ||
|
|
6638d64d68 | ||
|
|
eb412c2bcb | ||
|
|
fde4a59510 | ||
|
|
1ee394aa69 | ||
|
|
707f90573c | ||
|
|
f8429ad357 | ||
|
|
aa5003d297 | ||
|
|
e9901f5e58 | ||
|
|
f0d04a4a57 | ||
|
|
32e3f748d8 | ||
|
|
fa465a84df | ||
|
|
82cf1a4467 | ||
|
|
3bbd49dbad | ||
|
|
ad21857cae | ||
|
|
2d18b73138 | ||
|
|
e7bc18dad4 | ||
|
|
62e4eabab0 | ||
|
|
d204eebab9 | ||
|
|
ed5823151b | ||
|
|
968d7d1f11 | ||
|
|
086683319a | ||
|
|
e5c1f4847d | ||
|
|
087e114d34 | ||
|
|
5a6b8bb856 | ||
|
|
102f567cb5 | ||
|
|
1a64879b65 | ||
|
|
4a0e5e4b88 | ||
|
|
01857fb056 | ||
|
|
3afe4938f9 | ||
|
|
9e0544e735 | ||
|
|
3993c6ad6b | ||
|
|
649563457d | ||
|
|
6ec75714f0 | ||
|
|
15916e6668 | ||
|
|
76d0562716 | ||
|
|
2ecc4aead7 | ||
|
|
d8210847a4 | ||
|
|
ece916e12f | ||
|
|
1a29d4f60f |
@@ -863,7 +863,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/34064225?v=4",
|
||||
"profile": "https://github.com/CronKz",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -920,6 +921,735 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SjamonDaal",
|
||||
"name": "Djamon Staal",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4325936?v=4",
|
||||
"profile": "https://www.sdhd.nl/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EarlRamirez",
|
||||
"name": "Earl Ramirez",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/12306859?v=4",
|
||||
"profile": "https://github.com/EarlRamirez",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RichardRay",
|
||||
"name": "Richard Ray Thomas",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/8671456?v=4",
|
||||
"profile": "https://github.com/RichardRay",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "thelamer",
|
||||
"name": "Ryan Kuba",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1852688?v=4",
|
||||
"profile": "https://www.taisun.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ParadoxGuitarist",
|
||||
"name": "Brian Monroe",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/6751928?v=4",
|
||||
"profile": "https://github.com/ParadoxGuitarist",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "plexorama",
|
||||
"name": "plexorama",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/605167?v=4",
|
||||
"profile": "https://github.com/plexorama",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tilldeeke",
|
||||
"name": "Till Deeke",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1795149?v=4",
|
||||
"profile": "https://tilldeeke.de",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "5quirrel",
|
||||
"name": "5quirrel",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/12634129?v=4",
|
||||
"profile": "https://github.com/5quirrel",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jasonlshelton",
|
||||
"name": "Jason",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/13071957?v=4",
|
||||
"profile": "https://github.com/jasonlshelton",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chemfy",
|
||||
"name": "Antti",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/7128321?v=4",
|
||||
"profile": "https://github.com/chemfy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DeusMaximus",
|
||||
"name": "DeusMaximus",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/10080364?v=4",
|
||||
"profile": "https://github.com/DeusMaximus",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "A-ROYAL",
|
||||
"name": "a-royal",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/16384611?v=4",
|
||||
"profile": "https://github.com/A-ROYAL",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "albertoaldrigo",
|
||||
"name": "Alberto Aldrigo",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5358208?v=4",
|
||||
"profile": "https://github.com/albertoaldrigo",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "RealEnder",
|
||||
"name": "Alex Stanev",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1412342?v=4",
|
||||
"profile": "http://alex.stanev.org/blog",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sirrus",
|
||||
"name": "Andreas Rehm",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/177295?v=4",
|
||||
"profile": "http://devel.itsolution2.de",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xelan",
|
||||
"name": "Andreas Erhard",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5080535?v=4",
|
||||
"profile": "https://github.com/xelan",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "angeldeejay",
|
||||
"name": "Andrés Vanegas Jiménez",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/142350?v=4",
|
||||
"profile": "https://github.com/angeldeejay",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aschiavon91",
|
||||
"name": "Antonio Schiavon",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3910403?v=4",
|
||||
"profile": "https://github.com/aschiavon91",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benunter",
|
||||
"name": "benunter",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10464547?v=4",
|
||||
"profile": "https://github.com/benunter",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rudashi",
|
||||
"name": "Borys Żmuda",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5038647?v=4",
|
||||
"profile": "http://catweb24.pl",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "chibacityblues",
|
||||
"name": "chibacityblues",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5539359?v=4",
|
||||
"profile": "https://github.com/chibacityblues",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cwlin0416",
|
||||
"name": "Chien Wei Lin",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1954830?v=4",
|
||||
"profile": "https://github.com/cwlin0416",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Againstreality",
|
||||
"name": "Christian Schuster",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/11700533?v=4",
|
||||
"profile": "https://github.com/Againstreality",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kopi-item",
|
||||
"name": "Christian Stefanus",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/4308704?v=4",
|
||||
"profile": "http://chriss.webhostid.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wxcafe",
|
||||
"name": "wxcafé",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3009327?v=4",
|
||||
"profile": "http://wxcafe.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dpyroc",
|
||||
"name": "dpyroc",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/35761525?v=4",
|
||||
"profile": "https://github.com/dpyroc",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "da-friedl",
|
||||
"name": "Daniel Friedlmaier",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2153639?v=4",
|
||||
"profile": "http://www.friedlmaier.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danielheene",
|
||||
"name": "Daniel Heene",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2947640?v=4",
|
||||
"profile": "https://github.com/danielheene",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danielcb",
|
||||
"name": "danielcb",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/319022?v=4",
|
||||
"profile": "https://github.com/danielcb",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dominiksenti",
|
||||
"name": "Dominik Senti",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/15846537?v=4",
|
||||
"profile": "https://github.com/dominiksenti",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EpixFr",
|
||||
"name": "Eric Gautheron",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/25570954?v=4",
|
||||
"profile": "http://www.konectik.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Erlpil",
|
||||
"name": "Erlend Pilø",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5732623?v=4",
|
||||
"profile": "https://erlpil.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "frapposelli",
|
||||
"name": "Fabio Rapposelli",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/541832?v=4",
|
||||
"profile": "http://fabio.technology",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fgbs",
|
||||
"name": "Felipe Barros",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3605240?v=4",
|
||||
"profile": "https://github.com/fgbs",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "possebon",
|
||||
"name": "Fernando Possebon",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/257745?v=4",
|
||||
"profile": "https://github.com/possebon",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gdraque",
|
||||
"name": "gdraque",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/2540832?v=4",
|
||||
"profile": "https://github.com/gdraque",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "georgwallisch",
|
||||
"name": "Georg Wallisch",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/23440381?v=4",
|
||||
"profile": "https://github.com/georgwallisch",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jgroblesr85",
|
||||
"name": "Gerardo Robles",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/9852832?v=4",
|
||||
"profile": "https://github.com/jgroblesr85",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mrgluek",
|
||||
"name": "Gluek",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/11082640?v=4",
|
||||
"profile": "https://t.me/Gluek",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AdnanAbuShahad",
|
||||
"name": "AdnanAbuShahad",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/6847946?v=4",
|
||||
"profile": "https://github.com/AdnanAbuShahad",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hafidzi",
|
||||
"name": "Hafidzi My",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/3580608?v=4",
|
||||
"profile": "https://hafidzi.my",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fofwisdom",
|
||||
"name": "Harim Park",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/205521?v=4",
|
||||
"profile": "https://github.com/fofwisdom",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Kentsson",
|
||||
"name": "Henrik Kentsson",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3333841?v=4",
|
||||
"profile": "http://www.kentsson.se",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "husnulyaqien",
|
||||
"name": "Husnul Yaqien",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/36551034?v=4",
|
||||
"profile": "https://github.com/husnulyaqien",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "abaalkh",
|
||||
"name": "Ibrahim",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2372747?v=4",
|
||||
"profile": "http://abaalkhail.org",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "igolman",
|
||||
"name": "igolman",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1389334?v=4",
|
||||
"profile": "https://github.com/igolman",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "itangiang",
|
||||
"name": "itangiang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/3257070?v=4",
|
||||
"profile": "https://github.com/itangiang",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jarby1211",
|
||||
"name": "jarby1211",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/14814254?v=4",
|
||||
"profile": "https://github.com/jarby1211",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JohnWillker",
|
||||
"name": "Jhonn Willker",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/6719357?v=4",
|
||||
"profile": "http://jwillker.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "joxelito94",
|
||||
"name": "Jose",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/10983635?v=4",
|
||||
"profile": "https://github.com/joxelito94",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "laopangzi",
|
||||
"name": "laopangzi",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5206122?v=4",
|
||||
"profile": "https://github.com/laopangzi",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lstrojny",
|
||||
"name": "Lars Strojny",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/79707?v=4",
|
||||
"profile": "http://usrportage.de",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MarcosBL",
|
||||
"name": "MarcosBL",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/389801?v=4",
|
||||
"profile": "http://twitter.com/marcosbl",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mariejoyacajes",
|
||||
"name": "marie joy cajes",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/35664606?v=4",
|
||||
"profile": "https://github.com/mariejoyacajes",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "msjohansen",
|
||||
"name": "Mark S. Johansen",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/3052816?v=4",
|
||||
"profile": "http://www.markjohansen.dk",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "stubben",
|
||||
"name": "Martin Stub",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/982885?v=4",
|
||||
"profile": "http://martinstub.dk",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "meyerf99",
|
||||
"name": "Meyer Flavio",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/28959963?v=4",
|
||||
"profile": "https://github.com/meyerf99",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MicaelRodrigues",
|
||||
"name": "Micael Rodrigues",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/796443?v=4",
|
||||
"profile": "https://github.com/MicaelRodrigues",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mikaelssen",
|
||||
"name": "Mikael Rasmussen",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10481331?v=4",
|
||||
"profile": "http://rubixy.com/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "IxFail",
|
||||
"name": "IxFail",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1544552?v=4",
|
||||
"profile": "https://github.com/IxFail",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MohammedFota",
|
||||
"name": "Mohammed Fota",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/18483118?v=4",
|
||||
"profile": "http://www.mohammedfota.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "omego",
|
||||
"name": "Moayad Alserihi",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/227080?v=4",
|
||||
"profile": "https://github.com/omego",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "saymd",
|
||||
"name": "saymd",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1680266?v=4",
|
||||
"profile": "https://github.com/saymd",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pooot",
|
||||
"name": "Patrik Larsson",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1826808?v=4",
|
||||
"profile": "https://nordsken.se",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "drcryo",
|
||||
"name": "drcryo",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/20584746?v=4",
|
||||
"profile": "https://github.com/drcryo",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pawel1615",
|
||||
"name": "pawel1615",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/19408004?v=4",
|
||||
"profile": "https://github.com/pawel1615",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bodrovics",
|
||||
"name": "bodrovics",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/23340468?v=4",
|
||||
"profile": "https://github.com/bodrovics",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "priatna",
|
||||
"name": "priatna",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3257654?v=4",
|
||||
"profile": "https://github.com/priatna",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ProfFan",
|
||||
"name": "Fan Jiang",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/5358374?v=4",
|
||||
"profile": "https://amayume.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ragnarcx",
|
||||
"name": "ragnarcx",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22555451?v=4",
|
||||
"profile": "https://github.com/ragnarcx",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "reinvanhaaren",
|
||||
"name": "Rein van Haaren",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/18654582?v=4",
|
||||
"profile": "http://www.reinvanhaaren.nl/",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dheche",
|
||||
"name": "Teguh Dwicaksana",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/386672?v=4",
|
||||
"profile": "http://dheche.songolimo.net",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FRaccie",
|
||||
"name": "fraccie",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/2572552?v=4",
|
||||
"profile": "https://github.com/FRaccie",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vinzruzell",
|
||||
"name": "vinzruzell",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/35182720?v=4",
|
||||
"profile": "https://github.com/vinzruzell",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vipsystem",
|
||||
"name": "Kevin Austin",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7883603?v=4",
|
||||
"profile": "http://kevinaustin.com",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wira-sandy",
|
||||
"name": "Wira Sandy",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3861828?v=4",
|
||||
"profile": "http://azuraweb.xyz",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "GrayHoax",
|
||||
"name": "Илья",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/8663789?v=4",
|
||||
"profile": "https://github.com/GrayHoax",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "godusevpn",
|
||||
"name": "GodUseVPN",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/30119111?v=4",
|
||||
"profile": "https://github.com/godusevpn",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EngrZhou",
|
||||
"name": "周周",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/745576?v=4",
|
||||
"profile": "https://github.com/EngrZhou",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "takuy",
|
||||
"name": "Sam",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1631095?v=4",
|
||||
"profile": "https://github.com/takuy",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Azerothian",
|
||||
"name": "Azerothian",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/264022?v=4",
|
||||
"profile": "https://www.illisian.com.au",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
10
.env.example
10
.env.example
@@ -7,7 +7,6 @@ APP_KEY=ChangeMe
|
||||
APP_URL=null
|
||||
APP_TIMEZONE='UTC'
|
||||
APP_LOCALE=en
|
||||
BACKUP_ENV=false
|
||||
|
||||
# --------------------------------------------
|
||||
# REQUIRED: DATABASE SETTINGS
|
||||
@@ -73,13 +72,20 @@ ENABLE_CSP=false
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
CACHE_PREFIX=snipeit
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: REDIS SETTINGS
|
||||
# --------------------------------------------
|
||||
REDIS_HOST=null
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT-null
|
||||
REDIS_PORT=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: MEMCACHED SETTINGS
|
||||
# --------------------------------------------
|
||||
MEMCACHED_HOST=null
|
||||
MEMCACHED_PORT=null
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: AWS S3 SETTINGS
|
||||
|
||||
27
.github/stale.yml
vendored
27
.github/stale.yml
vendored
@@ -9,12 +9,35 @@ exemptLabels:
|
||||
- :woman_technologist: ready for dev
|
||||
- :moneybag: bounty
|
||||
- :hand: bug
|
||||
- "🔐 security"
|
||||
- "👩💻 ready for dev"
|
||||
- "💰 bounty"
|
||||
- "✋ bug"
|
||||
|
||||
exemptMilestones: true
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
only: issues
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
unmarkComment: >
|
||||
Okay, it looks like this issue or feature request might still be important. We'll re-open
|
||||
it for now. Thank you for letting us know!
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Is this still relevant? We haven't heard from anyone in a bit. If so,
|
||||
please comment with any updates or additional detail.
|
||||
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
recent activity. It will be closed if no further activity occurs. Don't
|
||||
take it personally, we just need to keep a handle on things. Thank you
|
||||
for your contributions!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not had
|
||||
recent activity. If you believe this is still an issue, please confirm that
|
||||
this issue is still happening in the most recent version of Snipe-IT and reply
|
||||
to this thread to re-open it.
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -50,3 +50,4 @@ tests/_support/_generated/*
|
||||
/storage/oauth-public.key
|
||||
|
||||
*.cache
|
||||
/public/storage
|
||||
|
||||
@@ -16,8 +16,13 @@ services:
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.2
|
||||
- 7.1.4
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 7.3
|
||||
|
||||
# execute any number of scripts before the test run, custom env's are available as variables
|
||||
before_script:
|
||||
|
||||
@@ -18,7 +18,9 @@ patch \
|
||||
curl \
|
||||
vim \
|
||||
git \
|
||||
cron \
|
||||
mysql-client \
|
||||
cron \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -1,5 +1,5 @@
|
||||
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeyhead) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
|
||||
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
|
||||
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
@@ -10,7 +10,7 @@ It is built on [Laravel 5.4](http://laravel.com).
|
||||
|
||||
Snipe-IT is actively developed and we're [releasing quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
|
||||
|
||||
__This is web-based software__. This means there there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
|
||||
__This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
|
||||
|
||||
-----
|
||||
|
||||
@@ -58,6 +58,7 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
|
||||
- [InQRy](https://github.com/Microsoft/InQRy) by [@Microsoft](https://github.com/Microsoft)
|
||||
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
|
||||
- [jamf2snipe](https://github.com/ParadoxGuitarist/jamf2snipe) by [@ParadoxGuitarist](https://github.com/ParadoxGuitarist) - Python script to sync assets between a JAMFPro instance and a Snipe-II instance
|
||||
|
||||
As these were created by third-parties, Snipe-IT cannot provide support for these project, and you should contact the developers directly if you need assistance. Additionally, Snipe-IT makes no guarantees as to the reliability, accuracy or maintainability of these libraries. Use at your own risk. :)
|
||||
|
||||
@@ -82,8 +83,19 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/16108587?v=3" width="110px;"/><br /><sub>Jason Spriggs</sub>](http://jasonspriggs.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [<img src="https://avatars2.githubusercontent.com/u/1134568?v=3" width="110px;"/><br /><sub>Nate Felton</sub>](http://n8felton.wordpress.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [<img src="https://avatars2.githubusercontent.com/u/14036694?v=3" width="110px;"/><br /><sub>Manasses Ferreira</sub>](http://homepages.dcc.ufmg.br/~manassesferreira)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [<img src="https://avatars0.githubusercontent.com/u/15913949?v=3" width="110px;"/><br /><sub>Steve</sub>](https://github.com/steveelwood)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [<img src="https://avatars1.githubusercontent.com/u/3361683?v=3" width="110px;"/><br /><sub>matc</sub>](http://twitter.com/matc)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [<img src="https://avatars3.githubusercontent.com/u/7405702?v=3" width="110px;"/><br /><sub>Cole R. Davis</sub>](http://www.davisracingteam.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [<img src="https://avatars2.githubusercontent.com/u/10167681?v=3" width="110px;"/><br /><sub>gibsonjoshua55</sub>](https://github.com/gibsonjoshua55)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://github.com/zwerch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [<img src="https://avatars0.githubusercontent.com/u/6961695?v=4" width="110px;"/><br /><sub>Iman</sub>](https://github.com/imanghafoori1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [<img src="https://avatars1.githubusercontent.com/u/6551003?v=4" width="110px;"/><br /><sub>Richard Hofman</sub>](https://github.com/richardhofman6)<br />[💻](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [<img src="https://avatars0.githubusercontent.com/u/3697569?v=4" width="110px;"/><br /><sub>gizzmojr</sub>](https://github.com/gizzmojr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [<img src="https://avatars3.githubusercontent.com/u/404729?v=4" width="110px;"/><br /><sub>Jenny Li</sub>](https://github.com/imjennyli)<br />[📖](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/869227?v=4" width="110px;"/><br /><sub>Geoff Young</sub>](https://github.com/GeoffYoung)<br />[💻](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [<img src="https://avatars3.githubusercontent.com/u/1068477?v=4" width="110px;"/><br /><sub>Elliot Blackburn</sub>](http://www.elliotblackburn.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/6357451?v=4" width="110px;"/><br /><sub>Tõnis Ormisson</sub>](http://andmemasin.eu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [<img src="https://avatars0.githubusercontent.com/u/449411?v=4" width="110px;"/><br /><sub>Nicolai Essig</sub>](http://www.nicolai-essig.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [<img src="https://avatars1.githubusercontent.com/u/14809698?v=4" width="110px;"/><br /><sub>Danielle</sub>](https://github.com/techincolor)<br />[📖](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/18545156?v=4" width="110px;"/><br /><sub>Lawrence</sub>](https://github.com/TheVakman)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>uknzaeinozpas</sub>](https://github.com/uknzaeinozpas)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [💻](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [<img src="https://avatars3.githubusercontent.com/u/422752?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/Gelob)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/10672546?v=4" width="110px;"/><br /><sub>vcordes79</sub>](https://github.com/vcordes79)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/27958330?v=4" width="110px;"/><br /><sub>fordster78</sub>](https://github.com/fordster78)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [<img src="https://avatars0.githubusercontent.com/u/34064225?v=4" width="110px;"/><br /><sub>CronKz</sub>](https://github.com/CronKz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") [🌍](#translation-CronKz "Translation") | [<img src="https://avatars1.githubusercontent.com/u/585486?v=4" width="110px;"/><br /><sub>Tim Bishop</sub>](https://github.com/tdb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [<img src="https://avatars2.githubusercontent.com/u/5384694?v=4" width="110px;"/><br /><sub>Sean McIlvenna</sub>](https://www.seanmcilvenna.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [<img src="https://avatars3.githubusercontent.com/u/36515590?v=4" width="110px;"/><br /><sub>cepacs</sub>](https://github.com/cepacs)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/37537300?v=4" width="110px;"/><br /><sub>lea-mink</sub>](https://github.com/lea-mink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [<img src="https://avatars0.githubusercontent.com/u/7140719?v=4" width="110px;"/><br /><sub>Hannah Tinkler</sub>](https://github.com/hannahtinkler)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/1086388?v=4" width="110px;"/><br /><sub>Doeke Zanstra</sub>](https://github.com/doekman)<br />[💻](https://github.com/snipe/snipe-it/commits?author=doekman "Code") | [<img src="https://avatars1.githubusercontent.com/u/4325936?v=4" width="110px;"/><br /><sub>Djamon Staal</sub>](https://www.sdhd.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SjamonDaal "Code") | [<img src="https://avatars3.githubusercontent.com/u/12306859?v=4" width="110px;"/><br /><sub>Earl Ramirez</sub>](https://github.com/EarlRamirez)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EarlRamirez "Code") | [<img src="https://avatars2.githubusercontent.com/u/8671456?v=4" width="110px;"/><br /><sub>Richard Ray Thomas</sub>](https://github.com/RichardRay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=RichardRay "Code") | [<img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="110px;"/><br /><sub>Ryan Kuba</sub>](https://www.taisun.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thelamer "Code") | [<img src="https://avatars1.githubusercontent.com/u/6751928?v=4" width="110px;"/><br /><sub>Brian Monroe</sub>](https://github.com/ParadoxGuitarist)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ParadoxGuitarist "Code") | [<img src="https://avatars1.githubusercontent.com/u/605167?v=4" width="110px;"/><br /><sub>plexorama</sub>](https://github.com/plexorama)<br />[💻](https://github.com/snipe/snipe-it/commits?author=plexorama "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/1795149?v=4" width="110px;"/><br /><sub>Till Deeke</sub>](https://tilldeeke.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tilldeeke "Code") | [<img src="https://avatars0.githubusercontent.com/u/12634129?v=4" width="110px;"/><br /><sub>5quirrel</sub>](https://github.com/5quirrel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=5quirrel "Code") | [<img src="https://avatars1.githubusercontent.com/u/13071957?v=4" width="110px;"/><br /><sub>Jason</sub>](https://github.com/jasonlshelton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jasonlshelton "Code") | [<img src="https://avatars3.githubusercontent.com/u/7128321?v=4" width="110px;"/><br /><sub>Antti</sub>](https://github.com/chemfy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chemfy "Code") | [<img src="https://avatars3.githubusercontent.com/u/10080364?v=4" width="110px;"/><br /><sub>DeusMaximus</sub>](https://github.com/DeusMaximus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DeusMaximus "Code") | [<img src="https://avatars2.githubusercontent.com/u/16384611?v=4" width="110px;"/><br /><sub>a-royal</sub>](https://github.com/A-ROYAL)<br />[🌍](#translation-A-ROYAL "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5358208?v=4" width="110px;"/><br /><sub>Alberto Aldrigo</sub>](https://github.com/albertoaldrigo)<br />[🌍](#translation-albertoaldrigo "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1412342?v=4" width="110px;"/><br /><sub>Alex Stanev</sub>](http://alex.stanev.org/blog)<br />[🌍](#translation-RealEnder "Translation") | [<img src="https://avatars0.githubusercontent.com/u/177295?v=4" width="110px;"/><br /><sub>Andreas Rehm</sub>](http://devel.itsolution2.de)<br />[🌍](#translation-sirrus "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5080535?v=4" width="110px;"/><br /><sub>Andreas Erhard</sub>](https://github.com/xelan)<br />[🌍](#translation-xelan "Translation") | [<img src="https://avatars2.githubusercontent.com/u/142350?v=4" width="110px;"/><br /><sub>Andrés Vanegas Jiménez</sub>](https://github.com/angeldeejay)<br />[🌍](#translation-angeldeejay "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3910403?v=4" width="110px;"/><br /><sub>Antonio Schiavon</sub>](https://github.com/aschiavon91)<br />[🌍](#translation-aschiavon91 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10464547?v=4" width="110px;"/><br /><sub>benunter</sub>](https://github.com/benunter)<br />[🌍](#translation-benunter "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5038647?v=4" width="110px;"/><br /><sub>Borys Żmuda</sub>](http://catweb24.pl)<br />[🌍](#translation-rudashi "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/5539359?v=4" width="110px;"/><br /><sub>chibacityblues</sub>](https://github.com/chibacityblues)<br />[🌍](#translation-chibacityblues "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1954830?v=4" width="110px;"/><br /><sub>Chien Wei Lin</sub>](https://github.com/cwlin0416)<br />[🌍](#translation-cwlin0416 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/11700533?v=4" width="110px;"/><br /><sub>Christian Schuster</sub>](https://github.com/Againstreality)<br />[🌍](#translation-Againstreality "Translation") | [<img src="https://avatars1.githubusercontent.com/u/4308704?v=4" width="110px;"/><br /><sub>Christian Stefanus</sub>](http://chriss.webhostid.com)<br />[🌍](#translation-kopi-item "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3009327?v=4" width="110px;"/><br /><sub>wxcafé</sub>](http://wxcafe.net)<br />[🌍](#translation-wxcafe "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35761525?v=4" width="110px;"/><br /><sub>dpyroc</sub>](https://github.com/dpyroc)<br />[🌍](#translation-dpyroc "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2153639?v=4" width="110px;"/><br /><sub>Daniel Friedlmaier</sub>](http://www.friedlmaier.net)<br />[🌍](#translation-da-friedl "Translation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/2947640?v=4" width="110px;"/><br /><sub>Daniel Heene</sub>](https://github.com/danielheene)<br />[🌍](#translation-danielheene "Translation") | [<img src="https://avatars3.githubusercontent.com/u/319022?v=4" width="110px;"/><br /><sub>danielcb</sub>](https://github.com/danielcb)<br />[🌍](#translation-danielcb "Translation") | [<img src="https://avatars3.githubusercontent.com/u/15846537?v=4" width="110px;"/><br /><sub>Dominik Senti</sub>](https://github.com/dominiksenti)<br />[🌍](#translation-dominiksenti "Translation") | [<img src="https://avatars0.githubusercontent.com/u/25570954?v=4" width="110px;"/><br /><sub>Eric Gautheron</sub>](http://www.konectik.com)<br />[🌍](#translation-EpixFr "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5732623?v=4" width="110px;"/><br /><sub>Erlend Pilø</sub>](https://erlpil.com)<br />[🌍](#translation-Erlpil "Translation") | [<img src="https://avatars0.githubusercontent.com/u/541832?v=4" width="110px;"/><br /><sub>Fabio Rapposelli</sub>](http://fabio.technology)<br />[🌍](#translation-frapposelli "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3605240?v=4" width="110px;"/><br /><sub>Felipe Barros</sub>](https://github.com/fgbs)<br />[🌍](#translation-fgbs "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/257745?v=4" width="110px;"/><br /><sub>Fernando Possebon</sub>](https://github.com/possebon)<br />[🌍](#translation-possebon "Translation") | [<img src="https://avatars3.githubusercontent.com/u/2540832?v=4" width="110px;"/><br /><sub>gdraque</sub>](https://github.com/gdraque)<br />[🌍](#translation-gdraque "Translation") | [<img src="https://avatars0.githubusercontent.com/u/23440381?v=4" width="110px;"/><br /><sub>Georg Wallisch</sub>](https://github.com/georgwallisch)<br />[🌍](#translation-georgwallisch "Translation") | [<img src="https://avatars1.githubusercontent.com/u/9852832?v=4" width="110px;"/><br /><sub>Gerardo Robles</sub>](https://github.com/jgroblesr85)<br />[🌍](#translation-jgroblesr85 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/11082640?v=4" width="110px;"/><br /><sub>Gluek</sub>](https://t.me/Gluek)<br />[🌍](#translation-mrgluek "Translation") | [<img src="https://avatars0.githubusercontent.com/u/6847946?v=4" width="110px;"/><br /><sub>AdnanAbuShahad</sub>](https://github.com/AdnanAbuShahad)<br />[🌍](#translation-AdnanAbuShahad "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3580608?v=4" width="110px;"/><br /><sub>Hafidzi My</sub>](https://hafidzi.my)<br />[🌍](#translation-hafidzi "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/205521?v=4" width="110px;"/><br /><sub>Harim Park</sub>](https://github.com/fofwisdom)<br />[🌍](#translation-fofwisdom "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3333841?v=4" width="110px;"/><br /><sub>Henrik Kentsson</sub>](http://www.kentsson.se)<br />[🌍](#translation-Kentsson "Translation") | [<img src="https://avatars0.githubusercontent.com/u/36551034?v=4" width="110px;"/><br /><sub>Husnul Yaqien</sub>](https://github.com/husnulyaqien)<br />[🌍](#translation-husnulyaqien "Translation") | [<img src="https://avatars1.githubusercontent.com/u/2372747?v=4" width="110px;"/><br /><sub>Ibrahim</sub>](http://abaalkhail.org)<br />[🌍](#translation-abaalkh "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1389334?v=4" width="110px;"/><br /><sub>igolman</sub>](https://github.com/igolman)<br />[🌍](#translation-igolman "Translation") | [<img src="https://avatars1.githubusercontent.com/u/3257070?v=4" width="110px;"/><br /><sub>itangiang</sub>](https://github.com/itangiang)<br />[🌍](#translation-itangiang "Translation") | [<img src="https://avatars2.githubusercontent.com/u/14814254?v=4" width="110px;"/><br /><sub>jarby1211</sub>](https://github.com/jarby1211)<br />[🌍](#translation-jarby1211 "Translation") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/6719357?v=4" width="110px;"/><br /><sub>Jhonn Willker</sub>](http://jwillker.com)<br />[🌍](#translation-JohnWillker "Translation") | [<img src="https://avatars2.githubusercontent.com/u/10983635?v=4" width="110px;"/><br /><sub>Jose</sub>](https://github.com/joxelito94)<br />[🌍](#translation-joxelito94 "Translation") | [<img src="https://avatars0.githubusercontent.com/u/5206122?v=4" width="110px;"/><br /><sub>laopangzi</sub>](https://github.com/laopangzi)<br />[🌍](#translation-laopangzi "Translation") | [<img src="https://avatars2.githubusercontent.com/u/79707?v=4" width="110px;"/><br /><sub>Lars Strojny</sub>](http://usrportage.de)<br />[🌍](#translation-lstrojny "Translation") | [<img src="https://avatars0.githubusercontent.com/u/389801?v=4" width="110px;"/><br /><sub>MarcosBL</sub>](http://twitter.com/marcosbl)<br />[🌍](#translation-MarcosBL "Translation") | [<img src="https://avatars3.githubusercontent.com/u/35664606?v=4" width="110px;"/><br /><sub>marie joy cajes</sub>](https://github.com/mariejoyacajes)<br />[🌍](#translation-mariejoyacajes "Translation") | [<img src="https://avatars2.githubusercontent.com/u/3052816?v=4" width="110px;"/><br /><sub>Mark S. Johansen</sub>](http://www.markjohansen.dk)<br />[🌍](#translation-msjohansen "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
8
Vagrantfile
vendored
8
Vagrantfile
vendored
@@ -5,6 +5,14 @@ SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit
|
||||
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.define "bionic" do |bionic|
|
||||
bionic.vm.box = "ubuntu/bionic64"
|
||||
bionic.vm.hostname = 'bionic'
|
||||
bionic.vm.network "public_network", bridge: NETWORK_BRIDGE
|
||||
bionic.vm.provision :shell, :inline => "wget #{SNIPEIT_SH_URL}"
|
||||
bionic.vm.provision :shell, :inline => "chmod 755 snipeit.sh"
|
||||
end
|
||||
|
||||
config.vm.define "xenial" do |xenial|
|
||||
xenial.vm.box = "ubuntu/xenial64"
|
||||
xenial.vm.hostname = 'xenial'
|
||||
|
||||
@@ -128,15 +128,21 @@ class LdapSync extends Command
|
||||
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
|
||||
$usernames = array();
|
||||
for ($i = 0; $i < $location_users["count"]; $i++) {
|
||||
$location_users[$i]["ldap_location_override"] = true;
|
||||
$location_users[$i]["location_id"] = $ldap_loc["id"];
|
||||
$usernames[] = $location_users[$i][$ldap_result_username][0];
|
||||
|
||||
if (array_key_exists($ldap_result_username, $location_users[$i])) {
|
||||
$location_users[$i]["ldap_location_override"] = true;
|
||||
$location_users[$i]["location_id"] = $ldap_loc["id"];
|
||||
$usernames[] = $location_users[$i][$ldap_result_username][0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Delete located users from the general group.
|
||||
foreach ($results as $key => $generic_entry) {
|
||||
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
|
||||
unset($results[$key]);
|
||||
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
|
||||
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
|
||||
unset($results[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +168,15 @@ class LdapSync extends Command
|
||||
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
|
||||
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
|
||||
|
||||
|
||||
// This is active directory, not regular LDAP
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
$enabled_accounts = [
|
||||
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224'
|
||||
];
|
||||
$item['activated'] = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
|
||||
|
||||
// Fall through to LDAP
|
||||
} else {
|
||||
$item['activated'] = 0;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ class ObjectImportCommand extends Command
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId($this->option('user_id'))
|
||||
->setUpdating($this->option('update'))
|
||||
->setShouldNotify($this->option('send-welcome'))
|
||||
->setUsernameFormat($this->option('username_format'));
|
||||
|
||||
$logFile = $this->option('logfile');
|
||||
@@ -172,6 +173,7 @@ class ObjectImportCommand extends Command
|
||||
array('web-importer', null, InputOption::VALUE_NONE, 'Internal: packages output for use with the web importer'),
|
||||
array('user_id', null, InputOption::VALUE_REQUIRED, 'ID of user creating items', 1),
|
||||
array('update', null, InputOption::VALUE_NONE, 'If a matching item is found, update item information'),
|
||||
array('send-welcome', null, InputOption::VALUE_NONE, 'Whether to send a welcome email to any new users that are created.'),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class PaveIt extends Command
|
||||
DB::statement('delete from accessories_users');
|
||||
DB::statement('delete from asset_logs');
|
||||
DB::statement('delete from asset_maintenances');
|
||||
DB::statement('delete from login_attempts');
|
||||
DB::statement('delete from asset_uploads');
|
||||
DB::statement('delete from action_logs');
|
||||
DB::statement('delete from checkout_requests');
|
||||
|
||||
@@ -63,6 +63,8 @@ class ResetDemoSettings extends Command
|
||||
$settings->time_display_format = 'g:iA';
|
||||
$settings->thumbnail_max_h = '30';
|
||||
$settings->locale = 'en';
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = 'on';
|
||||
$settings->save();
|
||||
|
||||
if ($user = User::where('username', '=', 'admin')->first()) {
|
||||
|
||||
120
app/Console/Commands/RestoreDeletedUsers.php
Normal file
120
app/Console/Commands/RestoreDeletedUsers.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\License;
|
||||
use DB;
|
||||
use Artisan;
|
||||
|
||||
class RestoreDeletedUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:restore-users {--start_date=} {--end_date=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Restore users, and any associated assets and license checkouts.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$start_date = $this->option('start_date');
|
||||
$end_date = $this->option('end_date');
|
||||
$asset_totals = 0;
|
||||
$license_totals = 0;
|
||||
$user_count = 0;
|
||||
|
||||
|
||||
if (($start_date=='') || ($end_date=='')) {
|
||||
$this->info('ERROR: All fields are required.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$users = User::whereBetween('deleted_at', [$start_date, $end_date])->withTrashed()->get();
|
||||
$this->info('There are '.$users->count().' users deleted between '.$start_date.' and '.$end_date);
|
||||
$this->warn('Making a backup!');
|
||||
Artisan::call('backup:run');
|
||||
|
||||
foreach ($users as $user) {
|
||||
$user_count++;
|
||||
$user_logs = Actionlog::where('target_id', $user->id)->where('target_type',User::class)
|
||||
->where('action_type','checkout')->with('item')->get();
|
||||
|
||||
$this->info($user_count.'. '.$user->username.' ('.$user->id.') was deleted at '.$user->deleted_at. ' and has '.$user_logs->count().' checkouts associated.');
|
||||
|
||||
foreach ($user_logs as $user_log) {
|
||||
$this->info(' * '.$user_log->item_type.': '.$user_log->item->name.' - item_id: '.$user_log->item_id);
|
||||
|
||||
if ($user_log->item_type==Asset::class) {
|
||||
$asset_totals++;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $user_log->item_id)
|
||||
->update(['assigned_to' => $user->id, 'assigned_type'=> User::class]);
|
||||
|
||||
$this->info(' ** Asset '.$user_log->item->id.' ('.$user_log->item->asset_tag.') restored to user '.$user->id.'');
|
||||
|
||||
} elseif ($user_log->item_type==License::class) {
|
||||
$license_totals++;
|
||||
|
||||
$avail_seat = DB::table('license_seats')->where('license_id','=',$user_log->item->id)
|
||||
->whereNull('assigned_to')->whereNull('asset_id')->whereBetween('updated_at', [$start_date, $end_date])->first();
|
||||
if ($avail_seat) {
|
||||
$this->info(' ** Allocating seat '.$avail_seat->id.' for this License');
|
||||
|
||||
DB::table('license_seats')
|
||||
->where('id', $avail_seat->id)
|
||||
->update(['assigned_to' => $user->id]);
|
||||
|
||||
} else {
|
||||
$this->warn('ERROR: No available seats for '.$user_log->item->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->warn('Restoring user '.$user->username.'!');
|
||||
$user->restore();
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->info($asset_totals.' assets affected');
|
||||
$this->info($license_totals.' licenses affected');
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -57,12 +57,12 @@ class SendExpectedCheckinAlerts extends Command
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipient = new \App\Models\Recipients\AlertRecipient();
|
||||
|
||||
if ($settings->alert_email!='') {
|
||||
$recipient->notify(new ExpectedCheckinAdminNotification($assets));
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use DB;
|
||||
use App\Notifications\ExpiringLicenseNotification;
|
||||
use App\Notifications\ExpiringAssetsNotification;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -44,88 +46,41 @@ class SendExpirationAlerts extends Command
|
||||
public function fire()
|
||||
{
|
||||
|
||||
// Expiring Assets
|
||||
$expiring_assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
$this->info(count($expiring_assets).' expiring assets');
|
||||
|
||||
$asset_data['count'] = count($expiring_assets);
|
||||
$asset_data['email_content'] ='';
|
||||
$now = date("Y-m-d");
|
||||
$settings = Setting::getSettings();
|
||||
$threshold = $settings->alert_interval;
|
||||
|
||||
|
||||
foreach ($expiring_assets as $asset) {
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
|
||||
|
||||
$expires = $asset->present()->warrantee_expires();
|
||||
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
|
||||
|
||||
if ($difference > 30) {
|
||||
$asset_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
|
||||
} else {
|
||||
$asset_data['email_content'] .= '<tr style="background-color:#d9534f;">';
|
||||
}
|
||||
$asset_data['email_content'] .= '<td><a href="'.config('app.url').'/hardware/'.e($asset->id).'/view">';
|
||||
$asset_data['email_content'] .= $asset->present()->name().'</a></td><td>'.e($asset->asset_tag).'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.e($asset->present()->warrantee_expires()).'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.$difference.' '.trans('mail.days').'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.($asset->supplier ? e($asset->supplier->name) : '').'</td>';
|
||||
$asset_data['email_content'] .= '<td>'.($asset->assignedTo ? e($asset->assignedTo->present()->name()) : '').'</td>';
|
||||
$asset_data['email_content'] .= '</tr>';
|
||||
}
|
||||
|
||||
// Expiring licenses
|
||||
$expiring_licenses = License::getExpiringLicenses(Setting::getSettings()->alert_interval);
|
||||
$this->info(count($expiring_licenses).' expiring licenses');
|
||||
|
||||
|
||||
$license_data['count'] = $expiring_licenses->count();
|
||||
$license_data['email_content'] = '';
|
||||
|
||||
foreach ($expiring_licenses as $license) {
|
||||
$expires = $license->expiration_date;
|
||||
$difference = round(abs(strtotime($expires) - strtotime($now))/86400);
|
||||
|
||||
if ($difference > 30) {
|
||||
$license_data['email_content'] .= '<tr style="background-color: #fcffa3;">';
|
||||
} else {
|
||||
$license_data['email_content'] .= '<tr style="background-color:#d9534f;">';
|
||||
}
|
||||
$license_data['email_content'] .= '<td><a href="'.route('licenses.show', $license->id).'">';
|
||||
$license_data['email_content'] .= $license->name.'</a></td>';
|
||||
$license_data['email_content'] .= '<td>'.$license->expiration_date.'</td>';
|
||||
$license_data['email_content'] .= '<td>'.$difference.' days</td>';
|
||||
$license_data['email_content'] .= '</tr>';
|
||||
}
|
||||
|
||||
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
|
||||
|
||||
|
||||
if (count($expiring_assets) > 0) {
|
||||
$this->info('Report sent to '.Setting::getSettings()->alert_email);
|
||||
\Mail::send('emails.expiring-assets-report', $asset_data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Expiring_Assets_Report'));
|
||||
});
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
// Expiring Assets
|
||||
$assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
if ($assets->count() > 0) {
|
||||
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
|
||||
}
|
||||
|
||||
if (count($expiring_licenses) > 0) {
|
||||
$this->info('Report sent to '.Setting::getSettings()->alert_email);
|
||||
\Mail::send('emails.expiring-licenses-report', $license_data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Expiring_Licenses_Report'));
|
||||
});
|
||||
// Expiring licenses
|
||||
$licenses = License::getExpiringLicenses($threshold);
|
||||
|
||||
|
||||
if ($licenses->count() > 0) {
|
||||
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
if (Setting::getSettings()->alert_email=='') {
|
||||
echo "Could not send email. No alert email configured in settings. \n";
|
||||
} elseif (Setting::getSettings()->alerts_enabled!=1) {
|
||||
echo "Alerts are disabled in the settings. No mail will be sent. \n";
|
||||
if ($settings->alert_email=='') {
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif ($settings->alerts_enabled!=1) {
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Setting;
|
||||
use DB;
|
||||
use Mail;
|
||||
use App\Helpers\Helper;
|
||||
use App\Notifications\InventoryAlert;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -42,25 +43,28 @@ class SendInventoryAlerts extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ((Setting::getSettings()->alert_email!='') && (Setting::getSettings()->alerts_enabled==1)) {
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$data['data'] = Helper::checkLowInventory();
|
||||
$data['count'] = count($data['data']);
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled==1)) {
|
||||
|
||||
if (count($data['data']) > 0) {
|
||||
\Mail::send('emails.low-inventory', $data, function ($m) {
|
||||
$m->to(explode(',', Setting::getSettings()->alert_email), Setting::getSettings()->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Low_Inventory_Report'));
|
||||
$items = Helper::checkLowInventory();
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
|
||||
if (($items) && (count($items) > 0)) {
|
||||
$this->info(trans_choice('mail.low_inventory_alert', count($items)));
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Setting::getSettings()->alert_email=='') {
|
||||
echo "Could not send email. No alert email configured in settings. \n";
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif (Setting::getSettings()->alerts_enabled!=1) {
|
||||
echo "Alerts are disabled in the settings. No mail will be sent. \n";
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class Kernel extends ConsoleKernel
|
||||
Commands\SyncAssetLocations::class,
|
||||
Commands\RegenerateAssetTags::class,
|
||||
Commands\SyncAssetCounters::class,
|
||||
Commands\RestoreDeletedUsers::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,17 @@ namespace App\Exceptions;
|
||||
use Exception;
|
||||
class CheckoutNotAllowed extends Exception
|
||||
{
|
||||
|
||||
private $errorMessage;
|
||||
|
||||
function __construct($errorMessage = null)
|
||||
{
|
||||
$this->errorMessage = $errorMessage;
|
||||
|
||||
parent::__construct($errorMessage);
|
||||
}
|
||||
public function __toString()
|
||||
{
|
||||
return "A checkout is not allowed under these circumstances";
|
||||
return is_null($this->errorMessage) ? "A checkout is not allowed under these circumstances" : $this->errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,95 +137,6 @@ class Helper
|
||||
return floatval($floatString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of models in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function modelList()
|
||||
{
|
||||
$models = AssetModel::with('manufacturer')->get();
|
||||
$model_array[''] = trans('general.select_model');
|
||||
foreach ($models as $model) {
|
||||
$model_array[$model->id] = $model->present()->modelName();
|
||||
}
|
||||
return $model_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of companies in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function companyList()
|
||||
{
|
||||
$company_list = array('' => trans('general.select_company')) + DB::table('companies')
|
||||
->orderBy('name', 'asc')
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
return $company_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of categories in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function categoryList($category_type = null)
|
||||
{
|
||||
$categories = Category::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('name', 'asc');
|
||||
if (!empty($category_type)) {
|
||||
$categories = $categories->where('category_type', '=', $category_type);
|
||||
}
|
||||
$category_list = array('' => trans('general.select_category')) + $categories->pluck('name', 'id')->toArray();
|
||||
return $category_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of categories in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function departmentList()
|
||||
{
|
||||
$departments = Department::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('name', 'asc');
|
||||
|
||||
return array('' => trans('general.select_department')) + $departments->pluck('name', 'id')->toArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of suppliers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function suppliersList()
|
||||
{
|
||||
$supplier_list = array('' => trans('general.select_supplier')) + Supplier::orderBy('name', 'asc')
|
||||
->orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $supplier_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of status labels in an array to make a dropdown menu
|
||||
*
|
||||
@@ -240,37 +151,6 @@ class Helper
|
||||
return $statuslabel_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of locations in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function locationsList()
|
||||
{
|
||||
$location_list = array('' => trans('general.select_location')) + Location::orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $location_list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of manufacturers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function manufacturerList()
|
||||
{
|
||||
$manufacturer_list = array('' => trans('general.select_manufacturer')) +
|
||||
Manufacturer::orderBy('name', 'asc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $manufacturer_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of status label types in an array to make a dropdown menu
|
||||
*
|
||||
@@ -289,24 +169,6 @@ class Helper
|
||||
return $statuslabel_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of managers in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function managerList()
|
||||
{
|
||||
$manager_list = array('' => trans('general.select_user')) +
|
||||
User::where('deleted_at', '=', null)
|
||||
->orderBy('last_name', 'asc')
|
||||
->orderBy('first_name', 'asc')->get()
|
||||
->pluck('complete_name', 'id')->toArray();
|
||||
|
||||
return $manager_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of depreciations in an array to make a dropdown menu
|
||||
*
|
||||
@@ -341,54 +203,6 @@ class Helper
|
||||
return $category_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function usersList()
|
||||
{
|
||||
$users_list = array( '' => trans('general.select_user')) +
|
||||
Company::scopeCompanyables(User::where('deleted_at', '=', null))
|
||||
->where('show_in_list', '=', 1)
|
||||
->orderBy('last_name', 'asc')
|
||||
->orderBy('first_name', 'asc')->get()
|
||||
->pluck('complete_name', 'id')->toArray();
|
||||
|
||||
return $users_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of assets in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return Array
|
||||
*/
|
||||
public static function assetsList()
|
||||
{
|
||||
$assets_list = array('' => trans('general.select_asset')) + Asset::orderBy('name', 'asc')
|
||||
->whereNull('deleted_at')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $assets_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detailed list of assets in an array to make a dropdown menu
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.5]
|
||||
* @return array
|
||||
*/
|
||||
public static function detailedAssetList()
|
||||
{
|
||||
$assets = array('' => trans('general.select_asset')) + Company::scopeCompanyables(Asset::with('assignedTo', 'model'), 'assets.company_id')->get()->pluck('detailed_name', 'id')->toArray();
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of custom fields in an array to make a dropdown menu
|
||||
*
|
||||
@@ -413,7 +227,7 @@ class Helper
|
||||
{
|
||||
$keys = array_keys(CustomField::$PredefinedFormats);
|
||||
$stuff = array_combine($keys, $keys);
|
||||
return $stuff+["" => trans('admin/custom_fields/general.custom_format')];
|
||||
return $stuff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -476,7 +290,7 @@ class Helper
|
||||
$all_count = 0;
|
||||
|
||||
foreach ($consumables as $consumable) {
|
||||
$avail = $consumable->qty - $consumable->consumable_assignment_count; //$consumable->numRemaining();
|
||||
$avail = $consumable->numRemaining();
|
||||
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
||||
if ($consumable->qty > 0) {
|
||||
$percent = number_format((($avail / $consumable->qty) * 100), 0);
|
||||
|
||||
@@ -166,10 +166,8 @@ class AccessoriesController extends Controller
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
|
||||
$image = $request->file('image');
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$file_name = "accessory-".str_random(18).'.'.$ext;
|
||||
|
||||
@@ -24,7 +24,7 @@ class AccessoriesController extends Controller
|
||||
$this->authorize('view', Accessory::class);
|
||||
$allowed_columns = ['id','name','model_number','eol','notes','created_at','min_amt','company_id'];
|
||||
|
||||
$accessories = Accessory::whereNull('accessories.deleted_at')->with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
$accessories = Accessory::with('category', 'company', 'manufacturer', 'users', 'location');
|
||||
|
||||
if ($request->has('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
|
||||
@@ -145,7 +145,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', AssetModel::class);
|
||||
$this->authorize('update', AssetModel::class);
|
||||
$assetmodel = AssetModel::findOrFail($id);
|
||||
$assetmodel->fill($request->all());
|
||||
$assetmodel->fieldset_id = $request->get("custom_fieldset_id");
|
||||
|
||||
@@ -95,7 +95,7 @@ class AssetsController extends Controller
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
|
||||
// These are used by the API to query against specific ID numbers.
|
||||
@@ -127,7 +127,7 @@ class AssetsController extends Controller
|
||||
|
||||
if (($request->has('assigned_to')) && ($request->has('assigned_type'))) {
|
||||
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
|
||||
->where('assets.assigned_type', '=', $request->input('assigned_type'));
|
||||
->where('assets.assigned_type', '=', $request->input('assigned_type'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
@@ -167,12 +167,12 @@ class AssetsController extends Controller
|
||||
break;
|
||||
case 'RTD':
|
||||
$assets->whereNull('assets.assigned_to')
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
break;
|
||||
case 'Undeployable':
|
||||
$assets->Undeployable();
|
||||
@@ -188,11 +188,11 @@ class AssetsController extends Controller
|
||||
case 'Requestable':
|
||||
$assets->where('assets.requestable', '=', 1)
|
||||
->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.deployable','=',1)
|
||||
->where('status_alias.pending','=',0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
|
||||
break;
|
||||
case 'Deployed':
|
||||
@@ -207,8 +207,8 @@ class AssetsController extends Controller
|
||||
$join->on('status_alias.id', "=", "assets.status_id")
|
||||
->where('status_alias.archived', '=', 0);
|
||||
});
|
||||
|
||||
// If there is a status ID, don't take show_archived_in_list into consideration
|
||||
|
||||
// If there is a status ID, don't take show_archived_in_list into consideration
|
||||
} else {
|
||||
$assets->join('status_labels AS status_alias',function ($join) {
|
||||
$join->on('status_alias.id', "=", "assets.status_id");
|
||||
@@ -233,8 +233,8 @@ class AssetsController extends Controller
|
||||
// This handles all of the pivot sorting (versus the assets.* fields
|
||||
// in the allowed_columns array)
|
||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||
|
||||
|
||||
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
@@ -291,7 +291,7 @@ class AssetsController extends Controller
|
||||
$this->authorize('view', $asset);
|
||||
return (new AssetsTransformer)->transformAsset($asset);
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 404);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
}
|
||||
|
||||
@@ -305,17 +305,18 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function showBySerial($serial)
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
if ($assets = Asset::with('assetstatus')->with('assignedTo')
|
||||
->withTrashed()->where('serial',$serial)->get()) {
|
||||
$this->authorize('view', $assets);
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 404);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Returns JSON with information about an asset for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -353,7 +354,7 @@ class AssetsController extends Controller
|
||||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id'
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived());
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(),'company_id', 'assets');
|
||||
|
||||
if ($request->has('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
@@ -378,7 +379,7 @@ class AssetsController extends Controller
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType()=='pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
}
|
||||
@@ -465,46 +466,18 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if ($asset = Asset::find($id)) {
|
||||
($request->has('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : '';
|
||||
($request->has('name')) ?
|
||||
$asset->name = $request->get('name') : '';
|
||||
($request->has('serial')) ?
|
||||
$asset->serial = $request->get('serial') : '';
|
||||
($request->has('model_id')) ?
|
||||
$asset->model_id = $request->get('model_id') : '';
|
||||
($request->has('order_number')) ?
|
||||
$asset->order_number = $request->get('order_number') : '';
|
||||
($request->has('notes')) ?
|
||||
$asset->notes = $request->get('notes') : '';
|
||||
($request->has('asset_tag')) ?
|
||||
$asset->asset_tag = $request->get('asset_tag') : '';
|
||||
($request->has('archived')) ?
|
||||
$asset->archived = $request->get('archived') : '';
|
||||
($request->has('status_id')) ?
|
||||
$asset->status_id = $request->get('status_id') : '';
|
||||
($request->has('warranty_months')) ?
|
||||
$asset->warranty_months = $request->get('warranty_months') : '';
|
||||
($request->has('purchase_cost')) ?
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost')) : '';
|
||||
($request->has('purchase_date')) ?
|
||||
$asset->purchase_date = $request->get('purchase_date') : '';
|
||||
($request->has('assigned_to')) ?
|
||||
$asset->assigned_to = $request->get('assigned_to') : '';
|
||||
($request->has('supplier_id')) ?
|
||||
$asset->supplier_id = $request->get('supplier_id') : '';
|
||||
($request->has('requestable')) ?
|
||||
$asset->requestable = $request->get('requestable') : '';
|
||||
($request->has('rtd_location_id')) ?
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id') : '';
|
||||
($request->has('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : '';
|
||||
($request->has('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
|
||||
|
||||
$asset->fill($request->all());
|
||||
|
||||
($request->has('model_id')) ?
|
||||
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
|
||||
($request->has('company_id')) ?
|
||||
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : null;
|
||||
($request->has('rtd_location_id')) ?
|
||||
$asset->location_id = $request->get('rtd_location_id') : null;
|
||||
|
||||
// Update custom fields
|
||||
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
|
||||
@@ -519,9 +492,9 @@ class AssetsController extends Controller
|
||||
if ($asset->save()) {
|
||||
|
||||
if (($request->has('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->has('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->has('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
@@ -631,7 +604,7 @@ class AssetsController extends Controller
|
||||
$expected_checkin = request('expected_checkin', null);
|
||||
$note = request('note', null);
|
||||
$asset_name = request('name', null);
|
||||
|
||||
|
||||
// Set the location ID to the RTD location id if there is one
|
||||
if ($asset->rtd_location_id!='') {
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
@@ -639,7 +612,7 @@ class AssetsController extends Controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
@@ -755,7 +728,8 @@ class AssetsController extends Controller
|
||||
*/
|
||||
public function requestable(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('viewRequestable', Asset::class);
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
|
||||
|
||||
@@ -92,7 +92,7 @@ class CategoriesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Category::class);
|
||||
$this->authorize('update', Category::class);
|
||||
$category = Category::findOrFail($id);
|
||||
$category->fill($request->all());
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class CompaniesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Company::class);
|
||||
$this->authorize('update', Company::class);
|
||||
$company = Company::findOrFail($id);
|
||||
$company->fill($request->all());
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class ComponentsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Component::class);
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')->whereNull('components.deleted_at')
|
||||
$components = Company::scopeCompanyables(Component::select('components.*')
|
||||
->with('company', 'location', 'category'));
|
||||
|
||||
if ($request->has('search')) {
|
||||
@@ -39,6 +39,10 @@ class ComponentsController extends Controller
|
||||
$components->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
$components->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
@@ -116,7 +120,7 @@ class ComponentsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Component::class);
|
||||
$this->authorize('update', Component::class);
|
||||
$component = Component::findOrFail($id);
|
||||
$component->fill($request->all());
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ class ConsumablesController extends Controller
|
||||
$this->authorize('index', Consumable::class);
|
||||
$consumables = Company::scopeCompanyables(
|
||||
Consumable::select('consumables.*')
|
||||
->whereNull('consumables.deleted_at')
|
||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
||||
);
|
||||
|
||||
@@ -121,7 +120,7 @@ class ConsumablesController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Consumable::class);
|
||||
$this->authorize('update', Consumable::class);
|
||||
$consumable = Consumable::findOrFail($id);
|
||||
$consumable->fill($request->all());
|
||||
|
||||
|
||||
@@ -57,9 +57,15 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', CustomField::class);
|
||||
$this->authorize('update', CustomField::class);
|
||||
$field = CustomField::findOrFail($id);
|
||||
$data = $request->all();
|
||||
|
||||
/**
|
||||
* Updated values for the field,
|
||||
* without the "field_encrypted" flag, preventing the change of encryption status
|
||||
* @var array
|
||||
*/
|
||||
$data = $request->except(['field_encrypted']);
|
||||
|
||||
$validator = Validator::make($data, $field->validationRules());
|
||||
if ($validator->fails()) {
|
||||
@@ -106,6 +112,9 @@ class CustomFieldsController extends Controller
|
||||
public function postReorder(Request $request, $id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('update', $fieldset);
|
||||
|
||||
$fields = array();
|
||||
$order_array = array();
|
||||
|
||||
@@ -125,7 +134,8 @@ class CustomFieldsController extends Controller
|
||||
|
||||
public function associate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
@@ -142,7 +152,8 @@ class CustomFieldsController extends Controller
|
||||
|
||||
public function disassociate(Request $request, $field_id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$fieldset_id = $request->input('fieldset_id');
|
||||
@@ -167,6 +178,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->fieldset->count() >0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Field is in use.'));
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', CustomFieldset::class);
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ class DepartmentsController extends Controller
|
||||
{
|
||||
$department = Department::findOrFail($id);
|
||||
|
||||
$this->authorize('delete', $department);
|
||||
|
||||
if ($department->users->count() > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/departments/message.assoc_users')));
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Depreciation::class);
|
||||
$this->authorize('update', Depreciation::class);
|
||||
$depreciation = Depreciation::findOrFail($id);
|
||||
$depreciation->fill($request->all());
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class GroupsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Group::class);
|
||||
$this->authorize('update', Group::class);
|
||||
$group = Group::findOrFail($id);
|
||||
$group->fill($request->all());
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ class LicensesController extends Controller
|
||||
$licenses->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->has('category_i')) {
|
||||
$licenses->where('category_i','=',$request->input('category_i'));
|
||||
if ($request->has('category_id')) {
|
||||
$licenses->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('depreciation_id')) {
|
||||
@@ -168,7 +168,7 @@ class LicensesController extends Controller
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
$this->authorize('edit', License::class);
|
||||
$this->authorize('update', License::class);
|
||||
|
||||
$license = License::findOrFail($id);
|
||||
$license->fill($request->all());
|
||||
@@ -223,6 +223,8 @@ class LicensesController extends Controller
|
||||
|
||||
if ($license = License::find($licenseId)) {
|
||||
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
|
||||
@@ -106,7 +106,26 @@ class LocationsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
$location = Location::with('parent', 'manager', 'childLocations')
|
||||
->select([
|
||||
'locations.id',
|
||||
'locations.name',
|
||||
'locations.address',
|
||||
'locations.address2',
|
||||
'locations.city',
|
||||
'locations.state',
|
||||
'locations.zip',
|
||||
'locations.country',
|
||||
'locations.parent_id',
|
||||
'locations.manager_id',
|
||||
'locations.created_at',
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency'
|
||||
])
|
||||
->withCount('assignedAssets')
|
||||
->withCount('assets')
|
||||
->withCount('users')->findOrFail($id);
|
||||
return (new LocationsTransformer)->transformLocation($location);
|
||||
}
|
||||
|
||||
@@ -122,7 +141,7 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
$location = Location::findOrFail($id);
|
||||
$location->fill($request->all());
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class ManufacturersController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer = Manufacturer::withCount('assets')->withCount('licenses')->withCount('consumables')->withCount('accessories')->findOrFail($id);
|
||||
return (new ManufacturersTransformer)->transformManufacturer($manufacturer);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class ManufacturersController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Manufacturer::class);
|
||||
$this->authorize('update', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$manufacturer->fill($request->all());
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ class ReportsController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('reports.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'target','location');
|
||||
|
||||
if ($request->has('search')) {
|
||||
|
||||
@@ -101,7 +101,7 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Statuslabel::class);
|
||||
$this->authorize('update', Statuslabel::class);
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
|
||||
$request->except('deployable', 'pending','archived');
|
||||
@@ -160,6 +160,7 @@ class StatuslabelsController extends Controller
|
||||
|
||||
public function getAssetCountByStatuslabel()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
$statuslabels = Statuslabel::with('assets')->groupBy('id')->withCount('assets')->get();
|
||||
|
||||
@@ -237,6 +238,8 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function checkIfDeployable($id) {
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
if ($statuslabel->getStatuslabelType()=='deployable') {
|
||||
return '1';
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class SuppliersController extends Controller
|
||||
|
||||
$suppliers = Supplier::select(
|
||||
array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
|
||||
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
|
||||
)->withCount('assets')->withCount('licenses')->withCount('accessories');
|
||||
|
||||
|
||||
if ($request->has('search')) {
|
||||
@@ -93,7 +93,7 @@ class SuppliersController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('edit', Supplier::class);
|
||||
$this->authorize('update', Supplier::class);
|
||||
$supplier = Supplier::findOrFail($id);
|
||||
$supplier->fill($request->all());
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class UsersController extends Controller
|
||||
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
|
||||
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
|
||||
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
|
||||
'country', 'zip'
|
||||
'country', 'zip', 'id'
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
@@ -142,7 +142,7 @@ class UsersController extends Controller
|
||||
'users.avatar',
|
||||
'users.email',
|
||||
]
|
||||
);
|
||||
)->where('show_in_list', '=', '1');
|
||||
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
@@ -191,7 +191,8 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function store(SaveUserRequest $request)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = new User;
|
||||
$user->fill($request->all());
|
||||
|
||||
@@ -199,6 +200,12 @@ class UsersController extends Controller
|
||||
$user->password = bcrypt($request->get('password', $tmp_pass));
|
||||
|
||||
if ($user->save()) {
|
||||
if ($request->has('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
|
||||
@@ -230,7 +237,8 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function update(SaveUserRequest $request, $id)
|
||||
{
|
||||
$this->authorize('edit', User::class);
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
$user = User::findOrFail($id);
|
||||
$user->fill($request->all());
|
||||
|
||||
@@ -289,7 +297,8 @@ class UsersController extends Controller
|
||||
public function assets($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets = Asset::where('assigned_to', '=', $id)->where('assigned_type', '=', User::class)->with('model')->get();
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
@@ -304,7 +313,7 @@ class UsersController extends Controller
|
||||
public function postTwoFactorReset(Request $request)
|
||||
{
|
||||
|
||||
$this->authorize('edit', User::class);
|
||||
$this->authorize('update', User::class);
|
||||
|
||||
if ($request->has('id')) {
|
||||
try {
|
||||
@@ -320,4 +329,17 @@ class UsersController extends Controller
|
||||
return response()->json(['message' => 'No ID provided'], 500);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info on the current user.
|
||||
*
|
||||
* @author [Juan Font] [<juanfontalonso@gmail.com>]
|
||||
* @since [v4.4.2]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getCurrentUserInfo(Request $request)
|
||||
{
|
||||
return response()->json($request->user());
|
||||
}
|
||||
}
|
||||
|
||||
104
app/Http/Controllers/AssetCheckinController.php
Normal file
104
app/Http/Controllers/AssetCheckinController.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetCheckinRequest;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AssetCheckinController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param string $backto
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function create($assetId, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckinRequest $request
|
||||
* @param int $assetId
|
||||
* @param null $backto
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetCheckinRequest $request, $assetId = null, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
if ($asset->assignedType() == Asset::USER) {
|
||||
$user = $asset->assignedTo;
|
||||
}
|
||||
if (is_null($target = $asset->assignedTo)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
|
||||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkout = null;
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->assigned_type = null;
|
||||
$asset->accepted = null;
|
||||
$asset->name = e($request->get('name'));
|
||||
|
||||
if ($request->has('status_id')) {
|
||||
$asset->status_id = e($request->get('status_id'));
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if ($request->has('location_id')) {
|
||||
$asset->location_id = e($request->get('location_id'));
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($asset->save()) {
|
||||
$logaction = $asset->logCheckin($target, e(request('note')));
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
|
||||
$data['last_name'] = get_class($target) == User::class ? $target->last_name : '';
|
||||
$data['item_name'] = $asset->present()->name();
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = $asset->asset_tag;
|
||||
$data['item_serial'] = $asset->serial;
|
||||
$data['note'] = $logaction->note;
|
||||
$data['manufacturer_name'] = $asset->model->manufacturer->name;
|
||||
$data['model_name'] = $asset->model->name;
|
||||
$data['model_number'] = $asset->model->model_number;
|
||||
|
||||
if ($backto=='user') {
|
||||
return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route("hardware.index")->with('error', trans('admin/hardware/message.checkin.error'));
|
||||
}
|
||||
}
|
||||
96
app/Http/Controllers/AssetCheckoutController.php
Normal file
96
app/Http/Controllers/AssetCheckoutController.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AssetCheckoutController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset out to a
|
||||
* user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function create($assetId)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find(e($assetId)))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'));
|
||||
}
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check out an asset to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckoutRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetCheckoutRequest $request, $assetId)
|
||||
{
|
||||
try {
|
||||
// Check if the asset exists
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
} elseif (!$asset->availableForCheckout()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
}
|
||||
$this->authorize('checkout', $asset);
|
||||
$admin = Auth::user();
|
||||
|
||||
$target = $this->determineCheckoutTarget($asset);
|
||||
if ($asset->is($target)) {
|
||||
throw new CheckoutNotAllowed('You cannot check an asset out to itself.');
|
||||
}
|
||||
$asset = $this->updateAssetLocation($asset, $target);
|
||||
|
||||
$checkout_at = date("Y-m-d H:i:s");
|
||||
if (($request->has('checkout_at')) && ($request->get('checkout_at')!= date("Y-m-d"))) {
|
||||
$checkout_at = $request->get('checkout_at');
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
if ($request->has('expected_checkin')) {
|
||||
$expected_checkin = $request->get('expected_checkin');
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
} catch (CheckoutNotAllowed $e) {
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
125
app/Http/Controllers/AssetFilesController.php
Normal file
125
app/Http/Controllers/AssetFilesController.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Upload a file to the server.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetFileRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function store(AssetFileRequest $request, $assetId = null)
|
||||
{
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$destinationPath = config('app.private_uploads').'/assets';
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
foreach ($request->file('file') as $file) {
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'hardware-'.$asset->id.'-'.str_random(8);
|
||||
$filename .= '-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
$asset->logUpload($filename, e($request->get('notes')));
|
||||
}
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function show($assetId = null, $fileId = null, $download = true)
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
if (!$log = Actionlog::find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
$file = $log->get_src('assets');
|
||||
|
||||
if ($log->action_type =='audit') {
|
||||
$file = $log->get_src('audits');
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return response('File '.$file.' not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
if ($download != 'true') {
|
||||
if ($contents = file_get_contents($file)) {
|
||||
return Response::make($contents)->header('Content-Type', mime_content_type($file));
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: "], 500);
|
||||
}
|
||||
return Response::download($file);
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function destroy($assetId = null, $fileId = null)
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
$this->authorize('update', $asset);
|
||||
$destinationPath = config('app.private_uploads').'/imports/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$log = Actionlog::find($fileId);
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
}
|
||||
@@ -162,6 +162,9 @@ class AssetMaintenancesController extends Controller
|
||||
// Redirect to the improvement management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (!$assetMaintenance->asset) {
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', 'The asset associated with this maintenance does not exist.');
|
||||
} elseif (!Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
@@ -120,46 +120,6 @@ class AssetModelsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($model->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores new Asset Model data created from the
|
||||
* modal form on the Asset Creation view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @param Request $request
|
||||
* @return String JSON
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
//COPYPASTA!!!! FIXME
|
||||
$this->authorize('create', AssetModel::class);
|
||||
$model = new AssetModel;
|
||||
|
||||
$settings=Input::all();
|
||||
$settings['eol']= null;
|
||||
|
||||
$model->name=$request->input('name');
|
||||
$model->manufacturer_id = $request->input('manufacturer_id');
|
||||
$model->category_id = $request->input('category_id');
|
||||
$model->model_number = $request->input('model_number');
|
||||
$model->user_id = Auth::id();
|
||||
$model->notes = $request->input('notes');
|
||||
$model->eol= null;
|
||||
|
||||
if ($request->input('fieldset_id')=='') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = e($request->input('fieldset_id'));
|
||||
}
|
||||
|
||||
if ($model->save()) {
|
||||
return JsonResponse::create($model);
|
||||
} else {
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($model->getErrors()->all('<li>:message</li>'), true)], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view containing the asset model edit form.
|
||||
*
|
||||
@@ -372,9 +332,7 @@ class AssetModelsController extends Controller
|
||||
|
||||
// Show the page
|
||||
$view = View::make('models/edit');
|
||||
$view->with('category_list', Helper::categoryList('asset'));
|
||||
$view->with('depreciation_list', Helper::depreciationList());
|
||||
$view->with('manufacturer_list', Helper::manufacturerList());
|
||||
$view->with('item', $model);
|
||||
$view->with('clone_model', $model_to_clone);
|
||||
return $view;
|
||||
@@ -408,7 +366,7 @@ class AssetModelsController extends Controller
|
||||
*/
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
|
||||
|
||||
$models_raw_array = Input::get('ids');
|
||||
|
||||
// Make sure some IDs have been selected
|
||||
@@ -433,13 +391,8 @@ class AssetModelsController extends Controller
|
||||
$nochange = ['NC' => 'No Change'];
|
||||
$fieldset_list = $nochange + Helper::customFieldsetList();
|
||||
$depreciation_list = $nochange + Helper::depreciationList();
|
||||
$category_list = $nochange + Helper::categoryList('asset');
|
||||
$manufacturer_list = $nochange + Helper::manufacturerList();
|
||||
|
||||
|
||||
return view('models/bulk-edit', compact('models'))
|
||||
->with('manufacturer_list', $manufacturer_list)
|
||||
->with('category_list', $category_list)
|
||||
->with('fieldset_list', $fieldset_list)
|
||||
->with('depreciation_list', $depreciation_list);
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the assets listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
* the content for the assets listing, which is generated in getDatatable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetController::getDatatable() method that generates the JSON response
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
@@ -79,24 +79,6 @@ class AssetsController extends Controller
|
||||
return view('hardware/index')->with('company', $company);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the assets table by asset tag, and redirects if it finds one
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getAssetByTag()
|
||||
{
|
||||
$topsearch = (Input::get('topsearch')=="true");
|
||||
|
||||
if (!$asset = Asset::where('asset_tag', '=', Input::get('assetTag'))->first()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
$this->authorize('view', $asset);
|
||||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to create a new asset.
|
||||
*
|
||||
@@ -110,12 +92,8 @@ class AssetsController extends Controller
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$view = View::make('hardware/edit')
|
||||
->with('supplier_list', Helper::suppliersList())
|
||||
->with('model_list', Helper::modelList())
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('item', new Asset)
|
||||
->with('manufacturer', Helper::manufacturerList()) //handled in modal now?
|
||||
->with('category', Helper::categoryList('asset')) //handled in modal now?
|
||||
->with('statuslabel_types', Helper::statusTypeList());
|
||||
|
||||
if ($request->has('model_id')) {
|
||||
@@ -126,12 +104,12 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process new asset form data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
* Validate and process new asset form data.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function store(AssetRequest $request)
|
||||
{
|
||||
$this->authorize(Asset::class);
|
||||
@@ -153,7 +131,7 @@ class AssetsController extends Controller
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = request('status_id', 0);
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = Helper::ParseFloat(Input::get('purchase_cost'));
|
||||
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', 0);
|
||||
@@ -217,7 +195,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Was the asset created?
|
||||
// Was the asset created?
|
||||
if ($asset->save()) {
|
||||
|
||||
|
||||
@@ -233,7 +211,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e(Input::get('name')), $location);
|
||||
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')), $location);
|
||||
}
|
||||
// Redirect to the asset listing page
|
||||
\Session::flash('success', trans('admin/hardware/message.create.success'));
|
||||
@@ -245,13 +223,13 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to edit an existing asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
* Returns a view that presents a form to edit an existing asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function edit($assetId = null)
|
||||
{
|
||||
if (!$item = Asset::find($assetId)) {
|
||||
@@ -262,20 +240,62 @@ class AssetsController extends Controller
|
||||
$this->authorize($item);
|
||||
|
||||
return view('hardware/edit', compact('item'))
|
||||
->with('model_list', Helper::modelList())
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('statuslabel_types', Helper::statusTypeList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate and process asset edit form.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
* Returns a view that presents information about an asset for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function show($assetId = null)
|
||||
{
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
$this->authorize('view', $asset);
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (isset($asset)) {
|
||||
$audit_log = Actionlog::where('action_type', '=', 'audit')
|
||||
->where('item_id', '=', $assetId)
|
||||
->where('item_type', '=', Asset::class)
|
||||
->orderBy('created_at', 'DESC')->first();
|
||||
|
||||
if ($asset->location) {
|
||||
$use_currency = $asset->location->currency;
|
||||
} else {
|
||||
if ($settings->default_currency!='') {
|
||||
$use_currency = $settings->default_currency;
|
||||
} else {
|
||||
$use_currency = trans('general.currency');
|
||||
}
|
||||
}
|
||||
|
||||
$qr_code = (object) array(
|
||||
'display' => $settings->qr_code == '1',
|
||||
'url' => route('qr_code/hardware', $asset->id)
|
||||
);
|
||||
|
||||
return view('hardware/view', compact('asset', 'qr_code', 'settings'))
|
||||
->with('use_currency', $use_currency)->with('audit_log', $audit_log);
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate and process asset edit form.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
|
||||
public function update(AssetRequest $request, $assetId = null)
|
||||
{
|
||||
@@ -323,7 +343,7 @@ class AssetsController extends Controller
|
||||
$asset->physical = '1';
|
||||
|
||||
// Update the image
|
||||
if (Input::has('image')) {
|
||||
if ($request->has('image')) {
|
||||
$image = $request->input('image');
|
||||
// See postCreate for more explaination of the following.
|
||||
$header = explode(';', $image, 2)[0];
|
||||
@@ -384,13 +404,13 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a given asset (mark as deleted).
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
* Delete a given asset (mark as deleted).
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function destroy($assetId)
|
||||
{
|
||||
// Check if the asset exists
|
||||
@@ -410,256 +430,33 @@ class AssetsController extends Controller
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset out to a
|
||||
* user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCheckout($assetId)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find(e($assetId)))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'));
|
||||
}
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
|
||||
// Get the dropdown of users and then pass it to the checkout view
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check out an asset to a user.
|
||||
* Searches the assets table by asset tag, and redirects if it finds one
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckoutRequest $request
|
||||
* @param int $assetId
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postCheckout(AssetCheckoutRequest $request, $assetId)
|
||||
public function getAssetByTag(Request $request)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
} elseif (!$asset->availableForCheckout()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
}
|
||||
$this->authorize('checkout', $asset);
|
||||
$admin = Auth::user();
|
||||
$topsearch = ($request->get('topsearch')=="true");
|
||||
|
||||
|
||||
// This item is checked out to a location
|
||||
if (request('checkout_to_type')=='location') {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
$asset->location_id = ($target) ? $target->id : '';
|
||||
|
||||
} elseif (request('checkout_to_type')=='asset') {
|
||||
|
||||
if (request('assigned_asset') == $assetId) {
|
||||
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
|
||||
}
|
||||
|
||||
$target = Asset::where('id','!=',$assetId)->find(request('assigned_asset'));
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
|
||||
// Override with the asset's location_id if it has one
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
}
|
||||
|
||||
} elseif (request('checkout_to_type')=='user') {
|
||||
// Fetch the target and set the asset's new location_id
|
||||
$target = User::find(request('assigned_user'));
|
||||
$asset->location_id = ($target) ? $target->location_id : '';
|
||||
}
|
||||
|
||||
// No valid target was found - error out
|
||||
if (!$target) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
|
||||
if ((Input::has('checkout_at')) && (Input::get('checkout_at')!= date("Y-m-d"))) {
|
||||
$checkout_at = Input::get('checkout_at');
|
||||
} else {
|
||||
$checkout_at = date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
if (Input::has('expected_checkin')) {
|
||||
$expected_checkin = Input::get('expected_checkin');
|
||||
} else {
|
||||
$expected_checkin = '';
|
||||
}
|
||||
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e(Input::get('note')), Input::get('name'))) {
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param string $backto
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getCheckin($assetId, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
if (!$asset = Asset::where('asset_tag', '=', $request->get('assetTag'))->first()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
return view('hardware/checkin', compact('asset'))->with('statusLabel_list', Helper::statusLabelList())->with('backto', $backto);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate and process the form data to check an asset back into inventory.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetCheckinRequest $request
|
||||
* @param int $assetId
|
||||
* @param null $backto
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postCheckin(AssetCheckinRequest $request, $assetId = null, $backto = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
if (is_null($asset = Asset::find($assetId))) {
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
$admin = Auth::user();
|
||||
if ($asset->assignedType() == Asset::USER) {
|
||||
$user = $asset->assignedTo;
|
||||
}
|
||||
if (is_null($target = $asset->assignedTo)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.already_checked_in'));
|
||||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkout = null;
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->assigned_type = null;
|
||||
$asset->accepted = null;
|
||||
$asset->name = e(Input::get('name'));
|
||||
|
||||
if (Input::has('status_id')) {
|
||||
$asset->status_id = e(Input::get('status_id'));
|
||||
}
|
||||
|
||||
$asset->location_id = $asset->rtd_location_id;
|
||||
|
||||
if (Input::has('location_id')) {
|
||||
$asset->location_id = e(Input::get('location_id'));
|
||||
}
|
||||
|
||||
// Was the asset updated?
|
||||
if ($asset->save()) {
|
||||
$logaction = $asset->logCheckin($target, e(request('note')));
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
$data['first_name'] = get_class($target) == User::class ? $target->first_name : '';
|
||||
$data['last_name'] = get_class($target) == User::class ? $target->last_name : '';
|
||||
$data['item_name'] = $asset->present()->name();
|
||||
$data['checkin_date'] = $logaction->created_at;
|
||||
$data['item_tag'] = $asset->asset_tag;
|
||||
$data['item_serial'] = $asset->serial;
|
||||
$data['note'] = $logaction->note;
|
||||
$data['manufacturer_name'] = $asset->model->manufacturer->name;
|
||||
$data['model_name'] = $asset->model->name;
|
||||
$data['model_number'] = $asset->model->model_number;
|
||||
|
||||
if ($backto=='user') {
|
||||
return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkin.success'));
|
||||
}
|
||||
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route("hardware.index")->with('error', trans('admin/hardware/message.checkin.error'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that presents information about an asset for detail view.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function show($assetId = null)
|
||||
{
|
||||
|
||||
$asset = Asset::withTrashed()->find($assetId);
|
||||
$this->authorize('view', $asset);
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
|
||||
|
||||
if (isset($asset)) {
|
||||
$audit_log = Actionlog::where('action_type', '=', 'audit')
|
||||
->where('item_id', '=', $assetId)
|
||||
->where('item_type', '=', Asset::class)
|
||||
->orderBy('created_at', 'DESC')->first();
|
||||
|
||||
if ($asset->location) {
|
||||
$use_currency = $asset->location->currency;
|
||||
} else {
|
||||
if ($settings->default_currency!='') {
|
||||
$use_currency = $settings->default_currency;
|
||||
} else {
|
||||
$use_currency = trans('general.currency');
|
||||
}
|
||||
}
|
||||
|
||||
$qr_code = (object) array(
|
||||
'display' => $settings->qr_code == '1',
|
||||
'url' => route('qr_code/hardware', $asset->id)
|
||||
);
|
||||
|
||||
return view('hardware/view', compact('asset', 'qr_code', 'settings'))
|
||||
->with('use_currency', $use_currency)->with('audit_log', $audit_log);
|
||||
}
|
||||
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a QR code for the asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Response
|
||||
*/
|
||||
* Return a QR code for the asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return Response
|
||||
*/
|
||||
public function getQrCode($assetId = null)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
@@ -719,13 +516,13 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to clone an asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
* Returns a view that presents a form to clone an asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getClone($assetId = null)
|
||||
{
|
||||
// Check if the asset exists
|
||||
@@ -743,9 +540,9 @@ class AssetsController extends Controller
|
||||
$asset->assigned_to = '';
|
||||
|
||||
return view('hardware/edit')
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('statuslabel_types', Helper::statusTypeList())
|
||||
->with('item', $asset);
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with('statuslabel_types', Helper::statusTypeList())
|
||||
->with('item', $asset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -844,15 +641,15 @@ class AssetsController extends Controller
|
||||
$item[$asset_tag][$batch_counter]['user_id'] = $user->id;
|
||||
|
||||
Actionlog::firstOrCreate(array(
|
||||
'item_id' => $asset->id,
|
||||
'item_type' => Asset::class,
|
||||
'user_id' => Auth::user()->id,
|
||||
'note' => 'Checkout imported by '.Auth::user()->present()->fullName().' from history importer',
|
||||
'target_id' => $item[$asset_tag][$batch_counter]['user_id'],
|
||||
'target_type' => User::class,
|
||||
'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'],
|
||||
'action_type' => 'checkout',
|
||||
));
|
||||
'item_id' => $asset->id,
|
||||
'item_type' => Asset::class,
|
||||
'user_id' => Auth::user()->id,
|
||||
'note' => 'Checkout imported by '.Auth::user()->present()->fullName().' from history importer',
|
||||
'target_id' => $item[$asset_tag][$batch_counter]['user_id'],
|
||||
'target_type' => User::class,
|
||||
'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'],
|
||||
'action_type' => 'checkout',
|
||||
));
|
||||
|
||||
$asset->assigned_to = $user->id;
|
||||
|
||||
@@ -885,14 +682,14 @@ class AssetsController extends Controller
|
||||
$asset_batch[$x]['real_checkin'] = $checkin_date;
|
||||
|
||||
Actionlog::firstOrCreate(array(
|
||||
'item_id' => $asset_batch[$x]['asset_id'],
|
||||
'item_type' => Asset::class,
|
||||
'user_id' => Auth::user()->id,
|
||||
'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer',
|
||||
'target_id' => null,
|
||||
'created_at' => $checkin_date,
|
||||
'action_type' => 'checkin'
|
||||
));
|
||||
'item_id' => $asset_batch[$x]['asset_id'],
|
||||
'item_type' => Asset::class,
|
||||
'user_id' => Auth::user()->id,
|
||||
'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer',
|
||||
'target_id' => null,
|
||||
'created_at' => $checkin_date,
|
||||
'action_type' => 'checkin'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -901,13 +698,13 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Retore a deleted asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
* Retore a deleted asset.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getRestore($assetId = null)
|
||||
{
|
||||
// Get asset information
|
||||
@@ -929,355 +726,6 @@ class AssetsController extends Controller
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upload a file to the server.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param AssetFileRequest $request
|
||||
* @param int $assetId
|
||||
* @return Redirect
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postUpload(AssetFileRequest $request, $assetId = null)
|
||||
{
|
||||
|
||||
if (!$asset = Asset::find($assetId)) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$destinationPath = config('app.private_uploads').'/assets';
|
||||
|
||||
if ($request->hasFile('assetfile')) {
|
||||
foreach ($request->file('assetfile') as $file) {
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'hardware-'.$asset->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
$asset->logUpload($filename, e(Input::get('notes')));
|
||||
}
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.upload.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function deleteFile($assetId = null, $fileId = null)
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
$this->authorize('update', $asset);
|
||||
$destinationPath = config('app.private_uploads').'/imports/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
$log = Actionlog::find($fileId);
|
||||
$full_filename = $destinationPath.'/'.$log->filename;
|
||||
if (file_exists($full_filename)) {
|
||||
unlink($destinationPath.'/'.$log->filename);
|
||||
}
|
||||
$log->delete();
|
||||
return redirect()->back()->with('success', trans('admin/hardware/message.deletefile.success'));
|
||||
}
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v1.0]
|
||||
* @return View
|
||||
*/
|
||||
public function displayFile($assetId = null, $fileId = null, $download = true)
|
||||
{
|
||||
|
||||
$asset = Asset::find($assetId);
|
||||
|
||||
if (isset($asset->id)) {
|
||||
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
if (!$log = Actionlog::find($fileId)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
$file = $log->get_src('assets');
|
||||
|
||||
if ($log->action_type =='audit') {
|
||||
$file = $log->get_src('audits');
|
||||
}
|
||||
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return response('File '.$file.' not found on server', 404)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
if ($download != 'true') {
|
||||
if ($contents = file_get_contents($file)) {
|
||||
return Response::make($contents)->header('Content-Type', $filetype);
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: "], 500);
|
||||
}
|
||||
return Response::download($file);
|
||||
}
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist', compact('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function postBulkEdit(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if (!$request->has('ids')) {
|
||||
return redirect()->back()->with('error', 'No assets selected');
|
||||
}
|
||||
|
||||
$asset_raw_array = $request->input('ids');
|
||||
foreach ($asset_raw_array as $asset_id => $value) {
|
||||
$asset_ids[] = $asset_id;
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('bulk_actions')) {
|
||||
if ($request->input('bulk_actions')=='labels') {
|
||||
$count = 0;
|
||||
return view('hardware/labels')
|
||||
->with('assets', Asset::find($asset_ids))
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('count', $count);
|
||||
} elseif ($request->input('bulk_actions')=='delete') {
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
// Bulk edit
|
||||
} elseif ($request->input('bulk_actions')=='edit') {
|
||||
return view('hardware/bulk')
|
||||
->with('assets', request('ids'))
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
->with(
|
||||
'companies_list',
|
||||
array('' => '') + array('clear' => trans('general.remove_company')) + Helper::companyList()
|
||||
);
|
||||
}
|
||||
}
|
||||
return redirect()->back()->with('error', 'No action selected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk edits
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function postBulkSave(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
\Log::debug($request->input('ids'));
|
||||
|
||||
if (($request->has('ids')) && (count($request->input('ids')) > 0)) {
|
||||
$assets = $request->input('ids');
|
||||
if (($request->has('purchase_date'))
|
||||
|| ($request->has('purchase_cost'))
|
||||
|| ($request->has('supplier_id'))
|
||||
|| ($request->has('order_number'))
|
||||
|| ($request->has('warranty_months'))
|
||||
|| ($request->has('rtd_location_id'))
|
||||
|| ($request->has('requestable'))
|
||||
|| ($request->has('company_id'))
|
||||
|| ($request->has('status_id'))
|
||||
|| ($request->has('model_id'))
|
||||
) {
|
||||
foreach ($assets as $key => $value) {
|
||||
$update_array = array();
|
||||
|
||||
if ($request->has('purchase_date')) {
|
||||
$update_array['purchase_date'] = $request->input('purchase_date');
|
||||
}
|
||||
if ($request->has('purchase_cost')) {
|
||||
$update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost'));
|
||||
}
|
||||
if ($request->has('supplier_id')) {
|
||||
$update_array['supplier_id'] = $request->input('supplier_id');
|
||||
}
|
||||
if ($request->has('model_id')) {
|
||||
$update_array['model_id'] = $request->input('model_id');
|
||||
}
|
||||
if ($request->has('company_id')) {
|
||||
if ($request->input('company_id')=="clear") {
|
||||
$update_array['company_id'] = null;
|
||||
} else {
|
||||
$update_array['company_id'] = $request->input('company_id');
|
||||
}
|
||||
}
|
||||
if ($request->has('order_number')) {
|
||||
$update_array['order_number'] = $request->input('order_number');
|
||||
}
|
||||
if ($request->has('warranty_months')) {
|
||||
$update_array['warranty_months'] = $request->input('warranty_months');
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('rtd_location_id')) {
|
||||
$update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
if (($request->has('update_real_loc'))
|
||||
&& (($request->input('update_real_loc')) == '1'))
|
||||
{
|
||||
$update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('status_id')) {
|
||||
$update_array['status_id'] = $request->input('status_id');
|
||||
}
|
||||
if ($request->has('requestable')) {
|
||||
$update_array['requestable'] = $request->input('requestable');
|
||||
}
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $key)
|
||||
->update($update_array);
|
||||
} // endforeach
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.update.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->route("hardware.index")->with('warning', trans('admin/hardware/message.update.nothing_updated'));
|
||||
} // endif
|
||||
return redirect()->route("hardware.index")->with('warning', trans('No assets selected, so nothing was updated.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk deleted.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function postBulkDelete()
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
if (Input::has('ids')) {
|
||||
$assets = Asset::find(Input::get('ids'));
|
||||
foreach ($assets as $asset) {
|
||||
$update_array['deleted_at'] = date('Y-m-d H:i:s');
|
||||
$update_array['assigned_to'] = null;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update($update_array);
|
||||
} // endforeach
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.delete.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->to("hardware")->with('info', trans('admin/hardware/message.delete.nothing_updated'));
|
||||
}
|
||||
|
||||
|
||||
public function getBulkCheckout()
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
// Filter out assets that are not deployable.
|
||||
|
||||
return view('hardware/bulk-checkout')
|
||||
->with('users_list', Helper::usersList());
|
||||
}
|
||||
|
||||
public function postBulkCheckout(Request $request)
|
||||
{
|
||||
$admin = Auth::user();
|
||||
// Find checkout to type
|
||||
if (request('checkout_to_type')=='location') {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
} elseif (request('checkout_to_type')=='asset') {
|
||||
$target = Asset::find(request('assigned_asset'));
|
||||
} elseif (request('checkout_to_type')=='user') {
|
||||
$target = User::find(request('assigned_user'));
|
||||
}
|
||||
if (!is_array(Input::get('selected_assets'))) {
|
||||
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
|
||||
}
|
||||
|
||||
$asset_ids = array_filter(Input::get('selected_assets'));
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
if ($target->id == $asset_id) {
|
||||
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
|
||||
}
|
||||
}
|
||||
if ((Input::has('checkout_at')) && (Input::get('checkout_at')!= date("Y-m-d"))) {
|
||||
$checkout_at = e(Input::get('checkout_at'));
|
||||
} else {
|
||||
$checkout_at = date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
if (Input::has('expected_checkin')) {
|
||||
$expected_checkin = e(Input::get('expected_checkin'));
|
||||
} else {
|
||||
$expected_checkin = '';
|
||||
}
|
||||
|
||||
|
||||
$errors = [];
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids) {
|
||||
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
$asset = Asset::find($asset_id);
|
||||
$this->authorize('checkout', $asset);
|
||||
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e(Input::get('note')), null);
|
||||
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = $target->location_id;
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
array_merge_recursive($errors, $asset->getErrors()->toArray());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!$errors) {
|
||||
// Redirect to the new asset page
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
|
||||
}
|
||||
|
||||
|
||||
public function quickScan()
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
@@ -1327,14 +775,14 @@ class AssetsController extends Controller
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
$file = $request->file('image');
|
||||
try {
|
||||
$destinationPath = config('app.private_uploads').'/audits';
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'audit-'.$asset->id.'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
try {
|
||||
$destinationPath = config('app.private_uploads').'/audits';
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'audit-'.$asset->id.'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $filename);
|
||||
|
||||
@@ -55,7 +55,10 @@ class ForgotPasswordController extends Controller
|
||||
// to send the link, we will examine the response then see the message we
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$response = $this->broker()->sendResetLink(
|
||||
$request->only('email')
|
||||
array_merge(
|
||||
$request->only('email'),
|
||||
['activated' => '1']
|
||||
)
|
||||
);
|
||||
|
||||
if ($response === \Password::RESET_LINK_SENT) {
|
||||
|
||||
@@ -68,48 +68,54 @@ class LoginController extends Controller
|
||||
{
|
||||
$remote_user = $request->server('REMOTE_USER');
|
||||
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
|
||||
LOG::debug("Authenticatiing via REMOTE_USER.");
|
||||
Log::debug("Authenticatiing via REMOTE_USER.");
|
||||
|
||||
$pos = strpos($remote_user, '\\');
|
||||
if ($pos > 0) {
|
||||
$remote_user = substr($remote_user, $pos + 1);
|
||||
};
|
||||
|
||||
try {
|
||||
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->first();
|
||||
LOG::debug("Remote user auth lookup complete");
|
||||
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
|
||||
Log::debug("Remote user auth lookup complete");
|
||||
if(!is_null($user)) Auth::login($user, true);
|
||||
} catch(Exception $e) {
|
||||
LOG::error("There was an error authenticating the Remote user: " . $e->getMessage());
|
||||
Log::debug("There was an error authenticating the Remote user: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loginViaLdap(Request $request)
|
||||
{
|
||||
LOG::debug("Binding user to LDAP.");
|
||||
Log::debug("Binding user to LDAP.");
|
||||
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
|
||||
if (!$ldap_user) {
|
||||
LOG::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
|
||||
Log::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
|
||||
throw new \Exception("Could not find user in LDAP directory");
|
||||
} else {
|
||||
LOG::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
|
||||
Log::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
|
||||
}
|
||||
|
||||
// Check if the user already exists in the database and was imported via LDAP
|
||||
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->first();
|
||||
LOG::debug("Local auth lookup complete");
|
||||
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->where('activated', '=', '1')->first();
|
||||
Log::debug("Local auth lookup complete");
|
||||
|
||||
// The user does not exist in the database. Try to get them from LDAP.
|
||||
// If user does not exist and authenticates successfully with LDAP we
|
||||
// will create it on the fly and sign in with default permissions
|
||||
if (!$user) {
|
||||
LOG::debug("Local user ".Input::get('username')." does not exist");
|
||||
LOG::debug("Creating local user ".Input::get('username'));
|
||||
Log::debug("Local user ".Input::get('username')." does not exist");
|
||||
Log::debug("Creating local user ".Input::get('username'));
|
||||
|
||||
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
|
||||
LOG::debug("Local user created.");
|
||||
Log::debug("Local user created.");
|
||||
} else {
|
||||
LOG::debug("Could not create local user.");
|
||||
Log::debug("Could not create local user.");
|
||||
throw new \Exception("Could not create local user");
|
||||
}
|
||||
// If the user exists and they were imported from LDAP already
|
||||
} else {
|
||||
LOG::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
|
||||
Log::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
|
||||
|
||||
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
|
||||
|
||||
@@ -155,7 +161,7 @@ class LoginController extends Controller
|
||||
|
||||
// Should we even check for LDAP users?
|
||||
if (Setting::getSettings()->ldap_enabled=='1') {
|
||||
LOG::debug("LDAP is enabled.");
|
||||
Log::debug("LDAP is enabled.");
|
||||
try {
|
||||
$user = $this->loginViaLdap($request);
|
||||
Auth::login($user, true);
|
||||
@@ -163,21 +169,21 @@ class LoginController extends Controller
|
||||
// If the user was unable to login via LDAP, log the error and let them fall through to
|
||||
// local authentication.
|
||||
} catch (\Exception $e) {
|
||||
LOG::error("There was an error authenticating the LDAP user: ".$e->getMessage());
|
||||
Log::debug("There was an error authenticating the LDAP user: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// If the user wasn't authenticated via LDAP, skip to local auth
|
||||
if (!$user) {
|
||||
LOG::debug("Authenticating user against database.");
|
||||
Log::debug("Authenticating user against database.");
|
||||
// Try to log the user in
|
||||
if (!Auth::attempt(Input::only('username', 'password'), Input::get('remember-me', 0))) {
|
||||
if (!Auth::attempt(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
|
||||
|
||||
if (!$lockedOut) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
}
|
||||
|
||||
LOG::debug("Local authentication failed.");
|
||||
Log::debug("Local authentication failed.");
|
||||
return redirect()->back()->withInput()->with('error', trans('auth/message.account_not_found'));
|
||||
} else {
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
@@ -36,4 +38,8 @@ class ResetPasswordController extends Controller
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
249
app/Http/Controllers/BulkAssetsController.php
Normal file
249
app/Http/Controllers/BulkAssetsController.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BulkAssetsController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function edit(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
if (!$request->has('ids')) {
|
||||
return redirect()->back()->with('error', 'No assets selected');
|
||||
}
|
||||
|
||||
$asset_ids = array_keys($request->input('ids'));
|
||||
|
||||
if ($request->has('bulk_actions')) {
|
||||
switch($request->input('bulk_actions')) {
|
||||
case 'labels':
|
||||
return view('hardware/labels')
|
||||
->with('assets', Asset::find($asset_ids))
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('count', 0);
|
||||
case 'delete':
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
case 'edit':
|
||||
return view('hardware/bulk')
|
||||
->with('assets', request('ids'))
|
||||
->with('statuslabel_list', Helper::statusLabelList());
|
||||
}
|
||||
}
|
||||
return redirect()->back()->with('error', 'No action selected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk edits
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return Redirect
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
\Log::debug($request->input('ids'));
|
||||
|
||||
if(!$request->has('ids') || count($request->input('ids')) <= 0) {
|
||||
return redirect()->route("hardware.index")->with('warning', trans('No assets selected, so nothing was updated.'));
|
||||
}
|
||||
|
||||
$assets = array_keys($request->input('ids'));
|
||||
|
||||
if (($request->has('purchase_date'))
|
||||
|| ($request->has('purchase_cost'))
|
||||
|| ($request->has('supplier_id'))
|
||||
|| ($request->has('order_number'))
|
||||
|| ($request->has('warranty_months'))
|
||||
|| ($request->has('rtd_location_id'))
|
||||
|| ($request->has('requestable'))
|
||||
|| ($request->has('company_id'))
|
||||
|| ($request->has('status_id'))
|
||||
|| ($request->has('model_id'))
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
$this->update_array = [];
|
||||
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('model_id')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
->conditionallyAddItem('status_id')
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months');
|
||||
|
||||
if ($request->has('purchase_cost')) {
|
||||
$this->update_array['purchase_cost'] = Helper::ParseFloat($request->input('purchase_cost'));
|
||||
}
|
||||
|
||||
if ($request->has('company_id')) {
|
||||
$this->update_array['company_id'] = $request->input('company_id');
|
||||
if ($request->input('company_id')=="clear") {
|
||||
$this->update_array['company_id'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('rtd_location_id')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
if (($request->has('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
}
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $assetId)
|
||||
->update($this->update_array);
|
||||
} // endforeach
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.update.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->route("hardware.index")->with('warning', trans('admin/hardware/message.update.nothing_updated'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Array to store update data per item
|
||||
* @var Array
|
||||
*/
|
||||
private $update_array;
|
||||
/**
|
||||
* Adds parameter to update array for an item if it exists in request
|
||||
* @param String $field field name
|
||||
* @return this Model for Chaining
|
||||
*/
|
||||
protected function conditionallyAddItem($field)
|
||||
{
|
||||
if(request()->has($field)) {
|
||||
$this->update_array[$field] = request()->input($field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk deleted.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param array $assets
|
||||
* @since [v2.0]
|
||||
*/
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
if ($request->has('ids')) {
|
||||
$assets = Asset::find($request->get('ids'));
|
||||
foreach ($assets as $asset) {
|
||||
$update_array['deleted_at'] = date('Y-m-d H:i:s');
|
||||
$update_array['assigned_to'] = null;
|
||||
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update($update_array);
|
||||
} // endforeach
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.delete.success'));
|
||||
// no values given, nothing to update
|
||||
}
|
||||
return redirect()->to("hardware")->with('info', trans('admin/hardware/message.delete.nothing_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Bulk Checkout Page
|
||||
* @return View View to checkout multiple assets
|
||||
*/
|
||||
public function showCheckout()
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
// Filter out assets that are not deployable.
|
||||
|
||||
return view('hardware/bulk-checkout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Multiple Checkout Request
|
||||
* @return View
|
||||
*/
|
||||
public function storeCheckout(Request $request)
|
||||
{
|
||||
try {
|
||||
$admin = Auth::user();
|
||||
|
||||
$target = $this->determineCheckoutTarget();
|
||||
|
||||
if (!is_array($request->get('selected_assets'))) {
|
||||
return redirect()->route('hardware/bulkcheckout')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
|
||||
}
|
||||
|
||||
$asset_ids = array_filter($request->get('selected_assets'));
|
||||
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
if ($target->id == $asset_id && request('checkout_to_type') =='asset') {
|
||||
return redirect()->back()->with('error', 'You cannot check an asset out to itself.');
|
||||
}
|
||||
}
|
||||
$checkout_at = date("Y-m-d H:i:s");
|
||||
if (($request->has('checkout_at')) && ($request->get('checkout_at')!= date("Y-m-d"))) {
|
||||
$checkout_at = e($request->get('checkout_at'));
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
|
||||
if ($request->has('expected_checkin')) {
|
||||
$expected_checkin = e($request->get('expected_checkin'));
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
|
||||
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$this->authorize('checkout', $asset);
|
||||
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), null);
|
||||
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = $target->location_id;
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->save();
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
array_merge_recursive($errors, $asset->getErrors()->toArray());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!$errors) {
|
||||
// Redirect to the new asset page
|
||||
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->to("hardware/bulk-checkout")->with('error', $e->getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Http/Controllers/CheckInOutRequest.php
Normal file
56
app/Http/Controllers/CheckInOutRequest.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
|
||||
trait CheckInOutRequest
|
||||
{
|
||||
/**
|
||||
* Find target for checkout
|
||||
* @return SnipeModel Target asset is being checked out to.
|
||||
*/
|
||||
protected function determineCheckoutTarget()
|
||||
{
|
||||
// This item is checked out to a location
|
||||
switch(request('checkout_to_type'))
|
||||
{
|
||||
case 'location':
|
||||
return Location::findOrFail(request('assigned_location'));
|
||||
case 'asset':
|
||||
return Asset::findOrFail(request('assigned_asset'));
|
||||
case 'user':
|
||||
return User::findOrFail(request('assigned_user'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the location of the asset passed in.
|
||||
* @param Asset $asset Asset being updated
|
||||
* @param SnipeModel $target Target with location
|
||||
* @return Asset Asset being updated
|
||||
*/
|
||||
protected function updateAssetLocation($asset, $target)
|
||||
{
|
||||
switch(request('checkout_to_type'))
|
||||
{
|
||||
case 'location':
|
||||
$asset->location_id = $target->id;
|
||||
break;
|
||||
case 'asset':
|
||||
$asset->location_id = $target->rtd_location_id;
|
||||
// Override with the asset's location_id if it has one
|
||||
if ($target->location_id!='') {
|
||||
$asset->location_id = $target->location_id;
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
$asset->location_id = $target->location_id;
|
||||
break;
|
||||
}
|
||||
return $asset;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,9 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('companies/index')->with('companies', Company::all());
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
return view('companies/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,6 +43,8 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
return view('companies/edit')->with('item', new Company);
|
||||
}
|
||||
|
||||
@@ -54,6 +58,8 @@ final class CompaniesController extends Controller
|
||||
*/
|
||||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize('create', Company::class);
|
||||
|
||||
$company = new Company;
|
||||
$company->name = $request->input('name');
|
||||
|
||||
@@ -90,6 +96,9 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('companies/edit')->with('item', $item);
|
||||
}
|
||||
|
||||
@@ -108,6 +117,8 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')->with('error', trans('admin/companies/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $company);
|
||||
|
||||
$company->name = $request->input('name');
|
||||
|
||||
$old_image = $company->image;
|
||||
@@ -164,6 +175,9 @@ final class CompaniesController extends Controller
|
||||
return redirect()->route('companies.index')
|
||||
->with('error', trans('admin/companies/message.not_found'));
|
||||
} else {
|
||||
|
||||
$this->authorize('delete', $company);
|
||||
|
||||
try {
|
||||
$company->delete();
|
||||
return redirect()->route('companies.index')
|
||||
|
||||
@@ -202,19 +202,6 @@ class ComponentsController extends Controller
|
||||
return redirect()->route('components.index')->with('success', trans('admin/components/message.delete.success'));
|
||||
}
|
||||
|
||||
public function postBulk($componentId = null)
|
||||
{
|
||||
//$this->authorize('checkout', $component)
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
public function postBulkSave($componentId = null)
|
||||
{
|
||||
//$this->authorize('edit', Component::class);
|
||||
echo 'Stubbed - not yet complete';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a view to display component information.
|
||||
*
|
||||
|
||||
@@ -37,6 +37,7 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', CustomField::class);
|
||||
|
||||
$fieldsets = CustomFieldset::with("fields", "models")->get();
|
||||
$fields = CustomField::with("fieldset")->get();
|
||||
@@ -57,6 +58,7 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', new CustomField());
|
||||
}
|
||||
@@ -72,6 +74,8 @@ class CustomFieldsController extends Controller
|
||||
*/
|
||||
public function store(CustomFieldRequest $request)
|
||||
{
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$field = new CustomField([
|
||||
"name" => $request->get("name"),
|
||||
"element" => $request->get("element"),
|
||||
@@ -83,7 +87,7 @@ class CustomFieldsController extends Controller
|
||||
]);
|
||||
|
||||
|
||||
if (!in_array(Input::get('format'), array_keys(CustomField::$PredefinedFormats))) {
|
||||
if ($request->has("custom_format")) {
|
||||
$field->format = e($request->get("custom_format"));
|
||||
} else {
|
||||
$field->format = e($request->get("format"));
|
||||
@@ -92,7 +96,6 @@ class CustomFieldsController extends Controller
|
||||
if ($field->save()) {
|
||||
return redirect()->route("fields.index")->with("success", trans('admin/custom_fields/message.field.create.success'));
|
||||
} else {
|
||||
// dd($field);
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.create.error'));
|
||||
}
|
||||
|
||||
@@ -110,6 +113,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
if ($field->fieldset()->detach($fieldset_id)) {
|
||||
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])->with("success", trans('admin/custom_fields/message.field.delete.success'));
|
||||
}
|
||||
@@ -128,6 +133,8 @@ class CustomFieldsController extends Controller
|
||||
{
|
||||
$field = CustomField::find($field_id);
|
||||
|
||||
$this->authorize('delete', $field);
|
||||
|
||||
if ($field->fieldset->count()>0) {
|
||||
return redirect()->back()->withErrors(['message' => "Field is in-use"]);
|
||||
} else {
|
||||
@@ -149,6 +156,9 @@ class CustomFieldsController extends Controller
|
||||
public function edit($id)
|
||||
{
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
return view("custom_fields.fields.edit")->with('field', $field);
|
||||
}
|
||||
|
||||
@@ -166,10 +176,12 @@ class CustomFieldsController extends Controller
|
||||
public function update(CustomFieldRequest $request, $id)
|
||||
{
|
||||
$field = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $field);
|
||||
|
||||
$field->name = e($request->get("name"));
|
||||
$field->element = e($request->get("element"));
|
||||
$field->field_values = e($request->get("field_values"));
|
||||
$field->field_encrypted = e($request->get("field_encrypted", 0));
|
||||
$field->user_id = Auth::user()->id;
|
||||
$field->help_text = $request->get("help_text");
|
||||
$field->show_in_email = $request->get("show_in_email", 0);
|
||||
|
||||
@@ -38,6 +38,8 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$cfset = CustomFieldset::with('fields')->where('id', '=', $id)->orderBy('id', 'ASC')->first();
|
||||
|
||||
$this->authorize('view', $cfset);
|
||||
|
||||
if ($cfset) {
|
||||
$custom_fields_list = ["" => "Add New Field to Fieldset"] + CustomField::pluck("name", "id")->toArray();
|
||||
|
||||
@@ -68,6 +70,8 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
return view("custom_fields.fieldsets.edit");
|
||||
}
|
||||
|
||||
@@ -81,6 +85,8 @@ class CustomFieldsetsController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
|
||||
$cfset = new CustomFieldset(
|
||||
[
|
||||
"name" => e($request->get("name")),
|
||||
@@ -141,6 +147,8 @@ class CustomFieldsetsController extends Controller
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('delete', $fieldset);
|
||||
|
||||
if ($fieldset) {
|
||||
$models = AssetModel::where("fieldset_id", "=", $id);
|
||||
if ($models->count() == 0) {
|
||||
@@ -169,6 +177,8 @@ class CustomFieldsetsController extends Controller
|
||||
|
||||
$set = CustomFieldset::find($id);
|
||||
|
||||
$this->authorize('update', $set);
|
||||
|
||||
foreach ($set->fields as $field) {
|
||||
if ($field->id == Input::get('field_id')) {
|
||||
return redirect()->route("fieldsets.show", [$id])->withInput()->withErrors(['field_id' => trans('admin/custom_fields/message.field.already_added')]);
|
||||
|
||||
@@ -83,6 +83,8 @@ class DepartmentsController extends Controller
|
||||
{
|
||||
$department = Department::find($id);
|
||||
|
||||
$this->authorize('view', $department);
|
||||
|
||||
if (isset($department->id)) {
|
||||
return view('departments/view', compact('department'));
|
||||
}
|
||||
@@ -100,6 +102,8 @@ class DepartmentsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
|
||||
return view('departments/edit')->with('item', new Department);
|
||||
}
|
||||
|
||||
@@ -118,6 +122,8 @@ class DepartmentsController extends Controller
|
||||
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('delete', $department);
|
||||
|
||||
if ($department->users->count() > 0) {
|
||||
return redirect()->to(route('departments.index'))->with('error', trans('admin/departments/message.assoc_users'));
|
||||
}
|
||||
@@ -141,16 +147,20 @@ class DepartmentsController extends Controller
|
||||
if (is_null($item = Department::find($id))) {
|
||||
return redirect()->back()->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('departments/edit', compact('item'));
|
||||
}
|
||||
|
||||
public function update(ImageUploadRequest $request, $id) {
|
||||
|
||||
$this->authorize('create', Department::class);
|
||||
if (is_null($department = Department::find($id))) {
|
||||
return redirect()->route('departments.index')->with('error', trans('admin/departments/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $department);
|
||||
|
||||
$department->fill($request->all());
|
||||
$department->manager_id = ($request->has('manager_id' ) ? $request->input('manager_id') : null);
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return view('depreciations/index', compact('depreciations'));
|
||||
return view('depreciations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +48,8 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return view('depreciations/edit')->with('item', new Depreciation);
|
||||
}
|
||||
@@ -62,6 +66,8 @@ class DepreciationsController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', Depreciation::class);
|
||||
|
||||
// create a new instance
|
||||
$depreciation = new Depreciation();
|
||||
// Depreciation data
|
||||
@@ -94,6 +100,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $item);
|
||||
|
||||
return view('depreciations/edit', compact('item'));
|
||||
}
|
||||
|
||||
@@ -116,6 +124,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('update', $depreciation);
|
||||
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
@@ -145,6 +155,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.not_found'));
|
||||
}
|
||||
|
||||
$this->authorize('delete', $depreciation);
|
||||
|
||||
if ($depreciation->has_models() > 0) {
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.assoc_users'));
|
||||
@@ -171,6 +183,8 @@ class DepreciationsController extends Controller
|
||||
return redirect()->route('depreciations.index')->with('error', trans('admin/depreciations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$this->authorize('view', $depreciation);
|
||||
|
||||
return view('depreciations/view', compact('depreciation'));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class GroupsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
// Show the page
|
||||
return view('groups/index', compact('groups'));
|
||||
return view('groups/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ class GroupsController extends Controller
|
||||
if ($group->save()) {
|
||||
return redirect()->route("groups.index")->with('success', trans('admin/groups/message.success.create'));
|
||||
}
|
||||
return redirect(route('groups.create'))->withInput()->withErrors($group->getErrors());
|
||||
return redirect()->back()->withInput()->withErrors($group->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +111,7 @@ class GroupsController extends Controller
|
||||
{
|
||||
$permissions = config('permissions');
|
||||
if (!$group = Group::find($id)) {
|
||||
return redirect()->route('groups')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
$group->name = e(Input::get('name'));
|
||||
$group->permissions = json_encode(Input::get('permission'));
|
||||
@@ -138,7 +138,7 @@ class GroupsController extends Controller
|
||||
{
|
||||
if (!config('app.lock_passwords')) {
|
||||
if (!$group = Group::find($id)) {
|
||||
return redirect()->route('groups')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
return redirect()->route('groups.index')->with('error', trans('admin/groups/message.group_not_found', compact('id')));
|
||||
}
|
||||
$group->delete();
|
||||
// Redirect to the group management page
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use Assets;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Input;
|
||||
@@ -264,31 +265,40 @@ class LicensesController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param Request $request
|
||||
* @param int $licenseId
|
||||
* @param int $seatId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postCheckout(Request $request, $licenseId)
|
||||
public function postCheckout(Request $request, $licenseId, $seatId = null)
|
||||
{
|
||||
|
||||
// Check that the license is valid
|
||||
if ($license = License::where('id',$licenseId)->first()) {
|
||||
|
||||
if ($license = License::where('id', $licenseId)->first()) {
|
||||
|
||||
// If the license is valid, check that there is an available seat
|
||||
if ($license->getAvailSeatsCountAttribute() < 1) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
|
||||
// Get the next available seat for this license
|
||||
$next = $license->freeSeat();
|
||||
|
||||
if (!$next) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
if (!$seatId) {
|
||||
// Get the next available seat for this license
|
||||
$next = $license->freeSeat();
|
||||
if (!$next) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
} else {
|
||||
$licenseSeat = LicenseSeat::where('id', '=', $seatId)->first();
|
||||
if (!$licenseSeat) {
|
||||
return redirect()->route('licenses.index')->with('error', 'License seat is not available for checkout');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$licenseSeat = LicenseSeat::where('id', '=', $next->id)->first()) {
|
||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$this->authorize('checkout', $license);
|
||||
|
||||
@@ -495,7 +505,7 @@ class LicensesController extends Controller
|
||||
* @param int $licenseId
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postUpload(Request $request, $licenseId = null)
|
||||
public function postUpload(AssetFileRequest $request, $licenseId = null)
|
||||
{
|
||||
$license = License::find($licenseId);
|
||||
// the license is valid
|
||||
@@ -504,18 +514,9 @@ class LicensesController extends Controller
|
||||
if (isset($license->id)) {
|
||||
$this->authorize('update', $license);
|
||||
|
||||
if (Input::hasFile('licensefile')) {
|
||||
if (Input::hasFile('file')) {
|
||||
|
||||
foreach (Input::file('licensefile') as $file) {
|
||||
|
||||
$rules = array(
|
||||
'licensefile' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar,rtf,xml,lic'
|
||||
);
|
||||
$validator = Validator::make(array('licensefile'=> $file), $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->with('error', trans('admin/licenses/message.upload.invalidfiles'));
|
||||
}
|
||||
foreach (Input::file('file') as $file) {
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = 'license-'.$license->id.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$upload_success = $file->move($destinationPath, $filename);
|
||||
|
||||
@@ -44,7 +44,7 @@ class LocationsController extends Controller
|
||||
$locations = Location::orderBy('created_at', 'DESC')->with('parent', 'assets', 'assignedassets')->get();
|
||||
|
||||
// Show the page
|
||||
return view('locations/index', compact('locations'));
|
||||
return view('locations/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -114,42 +114,6 @@ class LocationsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($location->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and stores a new location created via the Create Asset form modal.
|
||||
*
|
||||
* @todo Check if a Form Request would work better here.
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @see AssetsController::getCreate() method that makes the form
|
||||
* @since [v1.0]
|
||||
* @return String JSON
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$new['currency']=Setting::first()->default_currency;
|
||||
|
||||
// create a new location instance
|
||||
$location = new Location();
|
||||
|
||||
// Save the location data
|
||||
$location->name = $request->input('name');
|
||||
$location->currency = Setting::first()->default_currency; //e(Input::get('currency'));
|
||||
$location->address = ''; //e(Input::get('address'));
|
||||
// $location->address2 = e(Input::get('address2'));
|
||||
$location->city = $request->input('city');
|
||||
$location->state = '';//e(Input::get('state'));
|
||||
$location->country = $request->input('country');
|
||||
// $location->zip = e(Input::get('zip'));
|
||||
$location->user_id = Auth::id();
|
||||
|
||||
// Was the location created?
|
||||
if ($location->save()) {
|
||||
return JsonResponse::create($location);
|
||||
}
|
||||
// failure
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($location->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a form view to edit location information.
|
||||
@@ -162,7 +126,7 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function edit($locationId = null)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($item = Location::find($locationId))) {
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
@@ -175,8 +139,7 @@ class LocationsController extends Controller
|
||||
$location_options = array('' => 'Top Level') + $location_options;
|
||||
|
||||
return view('locations/edit', compact('item'))
|
||||
->with('location_options', $location_options)
|
||||
->with('manager_list', Helper::managerList());
|
||||
->with('location_options', $location_options);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +154,7 @@ class LocationsController extends Controller
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $locationId = null)
|
||||
{
|
||||
$this->authorize('edit', Location::class);
|
||||
$this->authorize('update', Location::class);
|
||||
// Check if the location exists
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
|
||||
@@ -36,7 +36,7 @@ class ManufacturersController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', Manufacturer::class);
|
||||
return view('manufacturers/index', compact('manufacturers'));
|
||||
return view('manufacturers/index');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ class ModalController extends Controller
|
||||
}
|
||||
|
||||
function model() {
|
||||
return view('modals.model')
|
||||
->with('manufacturer', Helper::manufacturerList())
|
||||
->with('category', Helper::categoryList('asset'));
|
||||
return view('modals.model');
|
||||
}
|
||||
|
||||
function statuslabel() {
|
||||
|
||||
@@ -45,15 +45,21 @@ class ProfileController extends Controller
|
||||
{
|
||||
|
||||
$user = Auth::user();
|
||||
$user->first_name = Input::get('first_name');
|
||||
$user->last_name = Input::get('last_name');
|
||||
$user->website = Input::get('website');
|
||||
$user->location_id = Input::get('location_id');
|
||||
$user->gravatar = Input::get('gravatar');
|
||||
$user->locale = Input::get('locale');
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->website = $request->input('website');
|
||||
$user->gravatar = $request->input('gravatar');
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en');
|
||||
}
|
||||
|
||||
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled=='1') && (!config('app.lock_passwords')))) {
|
||||
$user->two_factor_optin = Input::get('two_factor_optin', '0');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin', '0');
|
||||
}
|
||||
|
||||
if (Gate::allows('self.edit_location') && (!config('app.lock_passwords'))) {
|
||||
$user->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
if (Input::file('avatar')) {
|
||||
|
||||
@@ -26,6 +26,14 @@ use Illuminate\Http\Request;
|
||||
*/
|
||||
class ReportsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Checks for correct permissions
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->authorize('reports.view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the accessories report.
|
||||
@@ -400,6 +408,10 @@ class ReportsController extends Controller
|
||||
$header[] = 'Employee No.';
|
||||
}
|
||||
|
||||
if ($request->has('manager')) {
|
||||
$header[] = trans('admin/users/table.manager');
|
||||
}
|
||||
|
||||
if ($request->has('department')) {
|
||||
$header[] = trans('general.department');
|
||||
}
|
||||
@@ -591,8 +603,8 @@ class ReportsController extends Controller
|
||||
|
||||
|
||||
if ($request->has('assigned_to')) {
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? e($asset->assigned->getFullNameAttribute()) : ($asset->assigned ? e($asset->assigned->display_name) : '');
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : e($asset->assignedType());
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? $asset->assigned->getFullNameAttribute() : ($asset->assigned ? $asset->assigned->display_name : '');
|
||||
$row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : $asset->assignedType();
|
||||
}
|
||||
|
||||
if ($request->has('username')) {
|
||||
@@ -613,6 +625,14 @@ class ReportsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('manager')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
$row[] = (($asset->assignedto) && ($asset->assignedto->manager)) ? $asset->assignedto->manager->present()->fullName : '';
|
||||
} else {
|
||||
$row[] = ''; // Empty string if unassigned
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($request->has('department')) {
|
||||
if ($asset->checkedOutToUser()) {
|
||||
|
||||
@@ -334,6 +334,7 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
|
||||
$setting->load_remote = $request->input('load_remote', '0');
|
||||
$setting->unique_serial = $request->input('unique_serial', '0');
|
||||
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
|
||||
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
|
||||
$setting->dashboard_message = $request->input('dashboard_message');
|
||||
@@ -396,9 +397,12 @@ class SettingsController extends Controller
|
||||
$setting->brand = $request->input('brand', '1');
|
||||
$setting->header_color = $request->input('header_color');
|
||||
$setting->support_footer = $request->input('support_footer');
|
||||
$setting->version_footer = $request->input('version_footer');
|
||||
$setting->footer_text = $request->input('footer_text');
|
||||
$setting->skin = $request->input('skin');
|
||||
$setting->show_url_in_emails = $request->input('show_url_in_emails', '0');
|
||||
$setting->logo_print_assets = $request->input('logo_print_assets', '0');
|
||||
|
||||
|
||||
|
||||
// Only allow the site name and CSS to be changed if lock_passwords is false
|
||||
@@ -480,16 +484,16 @@ class SettingsController extends Controller
|
||||
$setting->two_factor_enabled = $request->input('two_factor_enabled');
|
||||
}
|
||||
|
||||
# remote user login
|
||||
$setting->login_remote_user_enabled = (int)$request->input('login_remote_user_enabled');
|
||||
$setting->login_common_disabled = (int)$request->input('login_common_disabled');
|
||||
$setting->login_remote_user_custom_logout_url = $request->input('login_remote_user_custom_logout_url');
|
||||
}
|
||||
|
||||
$setting->pwd_secure_uncommon = (int) $request->input('pwd_secure_uncommon');
|
||||
$setting->pwd_secure_min = (int) $request->input('pwd_secure_min');
|
||||
$setting->pwd_secure_complexity = '';
|
||||
|
||||
# remote user login
|
||||
$setting->login_remote_user_enabled = (int)$request->input('login_remote_user_enabled');
|
||||
$setting->login_common_disabled= (int)$request->input('login_common_disabled');
|
||||
$setting->login_remote_user_custom_logout_url = $request->input('login_remote_user_custom_logout_url');
|
||||
|
||||
if ($request->has('pwd_secure_complexity')) {
|
||||
$setting->pwd_secure_complexity = implode('|', $request->input('pwd_secure_complexity'));
|
||||
@@ -534,7 +538,9 @@ class SettingsController extends Controller
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
$setting->locale = $request->input('locale', 'en');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$setting->locale = $request->input('locale', 'en');
|
||||
}
|
||||
$setting->default_currency = $request->input('default_currency', '$');
|
||||
$setting->date_display_format = $request->input('date_display_format');
|
||||
$setting->time_display_format = $request->input('time_display_format');
|
||||
@@ -788,22 +794,34 @@ class SettingsController extends Controller
|
||||
|
||||
|
||||
|
||||
if (Input::has('labels_display_name')) {
|
||||
if ($request->has('labels_display_name')) {
|
||||
$setting->labels_display_name = 1;
|
||||
} else {
|
||||
$setting->labels_display_name = 0;
|
||||
}
|
||||
|
||||
if (Input::has('labels_display_serial')) {
|
||||
if ($request->has('labels_display_serial')) {
|
||||
$setting->labels_display_serial = 1;
|
||||
} else {
|
||||
$setting->labels_display_serial = 0;
|
||||
}
|
||||
|
||||
if (Input::has('labels_display_tag')) {
|
||||
if ($request->has('labels_display_tag')) {
|
||||
$setting->labels_display_tag = 1;
|
||||
} else {
|
||||
$setting->labels_display_tag = 0;
|
||||
}
|
||||
|
||||
if ($request->has('labels_display_tag')) {
|
||||
$setting->labels_display_tag = 1;
|
||||
} else {
|
||||
$setting->labels_display_tag = 0;
|
||||
}
|
||||
|
||||
if ($request->has('labels_display_model')) {
|
||||
$setting->labels_display_model = 1;
|
||||
} else {
|
||||
$setting->labels_display_model = 0;
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class StatuslabelsController extends Controller
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
return view('statuslabels.index', compact('statuslabels'));
|
||||
return view('statuslabels.index');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
@@ -103,36 +103,6 @@ class StatuslabelsController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($statusLabel->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Statuslabel::class);
|
||||
$statuslabel = new Statuslabel();
|
||||
if (!$request->has('statuslabel_types')) {
|
||||
return JsonResponse::create(["error" => trans('validation.statuslabel_type')], 500);
|
||||
}
|
||||
$statustype = Statuslabel::getStatuslabelTypesForDB(Input::get('statuslabel_types'));
|
||||
$statuslabel->name = Input::get('name');
|
||||
$statuslabel->user_id = Auth::id();
|
||||
$statuslabel->notes = '';
|
||||
$statuslabel->deployable = $statustype['deployable'];
|
||||
$statuslabel->pending = $statustype['pending'];
|
||||
$statuslabel->archived = $statustype['archived'];
|
||||
|
||||
|
||||
if ($statuslabel->isValid()) {
|
||||
$statuslabel->save();
|
||||
// Redirect to the new Statuslabel page
|
||||
return JsonResponse::create($statuslabel);
|
||||
}
|
||||
return JsonResponse::create(["error" => $statuslabel->getErrors()->first()], 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Statuslabel update.
|
||||
*
|
||||
|
||||
@@ -34,10 +34,9 @@ class SuppliersController extends Controller
|
||||
{
|
||||
// Grab all the suppliers
|
||||
$this->authorize('view', Supplier::class);
|
||||
$suppliers = Supplier::orderBy('created_at', 'DESC')->get();
|
||||
|
||||
// Show the page
|
||||
return view('suppliers/index', compact('suppliers'));
|
||||
return view('suppliers/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -97,23 +96,6 @@ class SuppliersController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($supplier->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function apiStore(Request $request)
|
||||
{
|
||||
$this->authorize('create', Supplier::class);
|
||||
$supplier = new Supplier;
|
||||
$supplier->name = $request->input('name');
|
||||
$supplier->user_id = Auth::id();
|
||||
|
||||
if ($supplier->save()) {
|
||||
return JsonResponse::create($supplier);
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: ".print_r($supplier->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplier update.
|
||||
*
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\AssetFileRequest;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Group;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use App\Models\User;
|
||||
use App\Models\Group;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
use Artisan;
|
||||
use Auth;
|
||||
use Config;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use Gate;
|
||||
use HTML;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
use Input;
|
||||
use Lang;
|
||||
use League\Csv\Reader;
|
||||
@@ -29,12 +33,9 @@ use Redirect;
|
||||
use Response;
|
||||
use Str;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use URL;
|
||||
use View;
|
||||
use Illuminate\Http\Request;
|
||||
use Gate;
|
||||
use Artisan;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Users for
|
||||
@@ -85,8 +86,11 @@ class UsersController extends Controller
|
||||
$userPermissions = Helper::selectedPermissionsArray($permissions, Input::old('permissions', array()));
|
||||
$permissions = $this->filterDisplayable($permissions);
|
||||
|
||||
$user = new User;
|
||||
$user->activated = 1;
|
||||
|
||||
return view('users/edit', compact('groups', 'userGroups', 'permissions', 'userPermissions'))
|
||||
->with('user', new User);
|
||||
->with('user', $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,7 +115,7 @@ class UsersController extends Controller
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', $user->activated);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle');
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
@@ -163,68 +167,7 @@ class UsersController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($user->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON handler for creating a user through a modal popup
|
||||
*
|
||||
* @todo Handle validation more graciously
|
||||
* @author [B. Wetherington] [<uberbrady@gmail.com>]
|
||||
* @since [v1.8]
|
||||
* @return string JSON
|
||||
*/
|
||||
public function apiStore(SaveUserRequest $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = new User;
|
||||
$inputs = Input::except('csrf_token', 'password_confirm', 'groups', 'email_user');
|
||||
$inputs['activated'] = true;
|
||||
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->username = $request->input('username');
|
||||
$user->email = $request->input('email');
|
||||
$user->department_id = $request->input('department_id', null);
|
||||
if ($request->has('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
$user->activated = true;
|
||||
|
||||
// Was the user created?
|
||||
if ($user->save()) {
|
||||
|
||||
if (Input::get('email_user') == 1) {
|
||||
// Send the credentials through email
|
||||
$data = array();
|
||||
$data['email'] = $request->input('email');
|
||||
$data['username'] = $request->input('username');
|
||||
$data['first_name'] = $request->input('first_name');
|
||||
$data['last_name'] = e($request->input('last_name'));
|
||||
$data['password'] = $request->input('password');
|
||||
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
|
||||
/*Mail::send('emails.send-login', $data, function ($m) use ($user) {
|
||||
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.welcome', ['name' => $user->first_name]));
|
||||
});*/
|
||||
}
|
||||
|
||||
return JsonResponse::create($user);
|
||||
|
||||
}
|
||||
return JsonResponse::create(["error" => "Failed validation: " . print_r($user->getErrors(), true)], 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the edit user form
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $permissions
|
||||
* @return View
|
||||
* @internal param int $id
|
||||
*/
|
||||
|
||||
private function filterDisplayable($permissions)
|
||||
{
|
||||
@@ -237,6 +180,15 @@ class UsersController extends Controller
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that displays the edit user form
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @param $permissions
|
||||
* @return View
|
||||
* @internal param int $id
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
|
||||
@@ -298,19 +250,16 @@ class UsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
} catch (UserNotFoundException $e) {
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
|
||||
// Only save groups if the user is a super user
|
||||
if (Auth::user()->isSuperUser()) {
|
||||
if ($request->has('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
} else {
|
||||
$user->groups()->sync(array());
|
||||
}
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
|
||||
|
||||
@@ -326,7 +275,7 @@ class UsersController extends Controller
|
||||
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
|
||||
$user->locale = $request->input('locale');
|
||||
$user->employee_num = $request->input('employee_num');
|
||||
$user->activated = $request->input('activated', $user->activated);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->jobtitle = $request->input('jobtitle', null);
|
||||
$user->phone = $request->input('phone');
|
||||
$user->location_id = $request->input('location_id', null);
|
||||
@@ -338,6 +287,7 @@ class UsersController extends Controller
|
||||
$user->city = $request->input('city', null);
|
||||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
|
||||
|
||||
@@ -382,7 +332,7 @@ class UsersController extends Controller
|
||||
{
|
||||
try {
|
||||
// Get user information
|
||||
$user = User::find($id);
|
||||
$user = User::findOrFail($id);
|
||||
// Authorize takes care of many of our logic checks now.
|
||||
$this->authorize('delete', User::class);
|
||||
|
||||
@@ -420,7 +370,7 @@ class UsersController extends Controller
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')->with('success', $success);
|
||||
} catch (UserNotFoundException $e) {
|
||||
} catch (ModelNotFoundException $e) {
|
||||
// Prepare the error message
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
// Redirect to the user management page
|
||||
@@ -551,7 +501,7 @@ class UsersController extends Controller
|
||||
if (($key = array_search(Auth::user()->id, $user_raw_array)) !== false) {
|
||||
unset($user_raw_array[$key]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
@@ -762,129 +712,6 @@ class UsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user import view
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getImport()
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
// Selected groups
|
||||
$selectedGroups = Input::old('groups', array());
|
||||
// Get all the available permissions
|
||||
$permissions = config('permissions');
|
||||
$selectedPermissions = Input::old('permissions', array('superuser' => -1));
|
||||
// Show the page
|
||||
return view('users/import', compact('selectedGroups', 'permissions', 'selectedPermissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user import file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postImport()
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
if (!ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath(Input::file('user_import_csv'));
|
||||
$csv->setNewline("\r\n");
|
||||
|
||||
if (Input::get('has_headers') == 1) {
|
||||
$csv->setOffset(1);
|
||||
}
|
||||
|
||||
$duplicates = '';
|
||||
|
||||
$nbInsert = $csv->each(function ($row) use ($duplicates) {
|
||||
|
||||
if (array_key_exists(2, $row)) {
|
||||
|
||||
if (Input::get('activate') == 1) {
|
||||
$activated = '1';
|
||||
} else {
|
||||
$activated = '0';
|
||||
}
|
||||
|
||||
$pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 15);
|
||||
|
||||
// Location
|
||||
if (array_key_exists('4', $row)) {
|
||||
$user_location_id = trim($row[4]);
|
||||
if ($user_location_id=='') {
|
||||
$user_location_id = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// Check if this email already exists in the system
|
||||
$user = User::where('username', $row[2])->first();
|
||||
if ($user) {
|
||||
$duplicates .= $row[2] . ', ';
|
||||
} else {
|
||||
|
||||
$newuser = array(
|
||||
'first_name' => trim(e($row[0])),
|
||||
'last_name' => trim(e($row[1])),
|
||||
'username' => trim(e($row[2])),
|
||||
'email' => trim(e($row[3])),
|
||||
'password' => bcrypt($pass),
|
||||
'activated' => $activated,
|
||||
'location_id' => trim(e($user_location_id)),
|
||||
'phone' => trim(e($row[5])),
|
||||
'jobtitle' => trim(e($row[6])),
|
||||
'employee_num' => trim(e($row[7])),
|
||||
'company_id' => Company::getIdForUser($row[8]),
|
||||
'permissions' => '{"user":1}',
|
||||
'notes' => 'Imported user'
|
||||
);
|
||||
|
||||
DB::table('users')->insert($newuser);
|
||||
|
||||
|
||||
if (((Input::get('email_user') == 1) && !config('app.lock_passwords'))) {
|
||||
// Send the credentials through email
|
||||
if ($row[3] != '') {
|
||||
$data = array();
|
||||
$data['email'] = trim(e($row[4]));
|
||||
$data['username'] = trim(e($row[2]));
|
||||
$data['first_name'] = trim(e($row[0]));
|
||||
$data['last_name'] = trim(e($row[1]));
|
||||
$data['password'] = $pass;
|
||||
|
||||
if ($newuser['email']) {
|
||||
$user = User::where('username', $row[2])->first();
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
|
||||
/*Mail::send('emails.send-login', $data, function ($m) use ($newuser) {
|
||||
$m->to($newuser['email'], $newuser['first_name'] . ' ' . $newuser['last_name']);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.welcome', ['name' => $newuser['first_name']]));
|
||||
});*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return redirect()->route('users.index')->with('duplicates', $duplicates)->with('success', 'Success');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return JSON response with a list of user details for the getIndex() view.
|
||||
*
|
||||
@@ -1011,8 +838,7 @@ class UsersController extends Controller
|
||||
return redirect()->route('users.index')->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return view('users/ldap')
|
||||
->with('location_list', Helper::locationsList());
|
||||
return view('users/ldap');
|
||||
}
|
||||
|
||||
|
||||
@@ -1156,7 +982,7 @@ class UsersController extends Controller
|
||||
*/
|
||||
public function printInventory($id)
|
||||
{
|
||||
|
||||
$this->authorize('view', User::class);
|
||||
$show_user = User::where('id',$id)->withTrashed()->first();
|
||||
$assets = Asset::where('assigned_to', $id)->where('assigned_type', User::class)->with('model', 'model.category')->get();
|
||||
$licenses = $show_user->licenses()->get();
|
||||
|
||||
@@ -73,7 +73,7 @@ class ViewAssetsController extends Controller
|
||||
$assets = Asset::with('model', 'defaultLoc', 'location', 'assignedTo', 'requests')->Hardware()->RequestableAssets()->get();
|
||||
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
|
||||
|
||||
return view('account/requestable-assets', compact('user', 'assets', 'models'));
|
||||
return view('account/requestable-assets', compact('assets', 'models'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\ReferrerPolicyHeader::class,
|
||||
\App\Http\Middleware\ContentSecurityPolicyHeader::class,
|
||||
\App\Http\Middleware\NosniffGuard::class,
|
||||
\App\Http\Middleware\CheckForSetup::class,
|
||||
\Fideloper\Proxy\TrustProxies::class,
|
||||
\App\Http\Middleware\CheckForSetup::class,
|
||||
\App\Http\Middleware\CheckForDebug::class,
|
||||
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ class AssetFileRequest extends Request
|
||||
{
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,jpeg,doc,docx,pdf,txt,zip,rar|max:'.$max_file_size,
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,lic|max:'.$max_file_size,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -46,13 +46,14 @@ class AssetRequest extends Request
|
||||
|
||||
$rules['asset_tag'] = ($settings->auto_increment_assets == '1') ? 'max:255' : 'required';
|
||||
|
||||
$model = AssetModel::find($this->request->get('model_id'));
|
||||
if($this->request->get('model_id') != '') {
|
||||
$model = AssetModel::find($this->request->get('model_id'));
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
$rules += $model->fieldset->validation_rules();
|
||||
if (($model) && ($model->fieldset)) {
|
||||
$rules += $model->fieldset->validation_rules();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ class ItemImportRequest extends FormRequest
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId(Auth::id())
|
||||
->setUpdating($this->has('import-update'))
|
||||
->setShouldNotify($this->has('send-welcome'))
|
||||
->setUsernameFormat('firstname.lastname')
|
||||
->setFieldMappings($fieldMappings);
|
||||
// $logFile = storage_path('logs/importer.log');
|
||||
@@ -60,7 +61,7 @@ class ItemImportRequest extends FormRequest
|
||||
|
||||
public function log($string)
|
||||
{
|
||||
// \Log::Info($string);
|
||||
\Log::Info($string);
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
|
||||
22
app/Http/Traits/UniqueSerialTrait.php
Normal file
22
app/Http/Traits/UniqueSerialTrait.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace App\Http\Traits;
|
||||
|
||||
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 = \App\Models\Setting::first()) {
|
||||
if ($settings->unique_serial=='1') {
|
||||
return 'unique_undeleted:'.$this->table.','. $this->getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,11 @@ class AssetMaintenancesTransformer
|
||||
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
|
||||
'asset_tag'=> e($assetmaintenance->asset->asset_tag)
|
||||
|
||||
] : null,
|
||||
'company' => (($assetmaintenance->asset) && ($assetmaintenance->asset->company)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->company->id,
|
||||
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
|
||||
|
||||
] : null,
|
||||
'title' => ($assetmaintenance->title) ? e($assetmaintenance->title) : null,
|
||||
'location' => (($assetmaintenance->asset) && ($assetmaintenance->asset->location)) ? [
|
||||
@@ -38,9 +43,9 @@ class AssetMaintenancesTransformer
|
||||
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id,'name'=> e($assetmaintenance->supplier->name)] : null,
|
||||
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
||||
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
||||
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'datetime'),
|
||||
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
|
||||
'asset_maintenance_time' => $assetmaintenance->asset_maintenance_time,
|
||||
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'datetime'),
|
||||
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'date'),
|
||||
'user_id' => ($assetmaintenance->admin) ? ['id' => $assetmaintenance->admin->id,'name'=> e($assetmaintenance->admin->getFullNameAttribute())] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($assetmaintenance->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->updated_at, 'datetime'),
|
||||
|
||||
@@ -41,7 +41,7 @@ class CategoriesTransformer
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', Category::class) ? true : false,
|
||||
'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0)) ? true : false,
|
||||
'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0) && ($category->licenses_count == 0)) ? true : false,
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\License;
|
||||
use Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
@@ -32,12 +32,6 @@ class SelectlistTransformer
|
||||
|
||||
}
|
||||
|
||||
// This is weird and awful, but the only way I can find to allow the user to
|
||||
// clear the selection - @snipe
|
||||
if (count($items_array) > 0) {
|
||||
array_unshift($items_array, ['id' =>'', 'text'=> trans('general.clear_selection')]);
|
||||
}
|
||||
|
||||
$results = [
|
||||
'items' => $items_array,
|
||||
'pagination' =>
|
||||
|
||||
@@ -9,13 +9,13 @@ use App\Helpers\Helper;
|
||||
class StatuslabelsTransformer
|
||||
{
|
||||
|
||||
public function transformStatuslabels (Collection $statuslabels)
|
||||
public function transformStatuslabels (Collection $statuslabels, $total)
|
||||
{
|
||||
$array = array();
|
||||
foreach ($statuslabels as $statuslabel) {
|
||||
$array[] = self::transformStatuslabel($statuslabel);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array);
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformStatuslabel (Statuslabel $statuslabel)
|
||||
|
||||
@@ -81,8 +81,8 @@ class AssetImporter extends ItemImporter
|
||||
|
||||
// We need to save the user if it exists so that we can checkout to user later.
|
||||
// Sanitizing the item will remove it.
|
||||
if(array_key_exists('user', $this->item)) {
|
||||
$user = $this->item['user'];
|
||||
if(array_key_exists('checkout_target', $this->item)) {
|
||||
$target = $this->item['checkout_target'];
|
||||
}
|
||||
$item = $this->sanitizeItemForStoring($asset, $editingAsset);
|
||||
// The location id fetched by the csv reader is actually the rtd_location_id.
|
||||
@@ -112,9 +112,9 @@ class AssetImporter extends ItemImporter
|
||||
$asset->logCreate('Imported using csv importer');
|
||||
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
|
||||
|
||||
// If we have a user to checkout to, lets do so.
|
||||
if(isset($user)) {
|
||||
$asset->fresh()->checkOut($user);
|
||||
// If we have a target to checkout to, lets do so.
|
||||
if(isset($target)) {
|
||||
$asset->fresh()->checkOut($target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,17 +14,18 @@ class ConsumableImporter extends ItemImporter
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row); // TODO: Change the autogenerated stub
|
||||
$this->createConsumableIfNotExists();
|
||||
parent::handle($row);
|
||||
$this->createConsumableIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a consumable if a duplicate does not exist
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @param array $row CSV Row Being parsed.
|
||||
* @since 3.0
|
||||
*/
|
||||
public function createConsumableIfNotExists()
|
||||
public function createConsumableIfNotExists($row)
|
||||
{
|
||||
$consumable = Consumable::where('name', $this->item['name'])->first();
|
||||
if ($consumable) {
|
||||
@@ -39,6 +40,8 @@ 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");
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$consumable->unsetEventDispatcher();
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Importer;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Models\Department;
|
||||
use ForceUTF8\Encoding;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -29,9 +30,13 @@ abstract class Importer
|
||||
*/
|
||||
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',
|
||||
@@ -57,7 +62,15 @@ abstract class Importer
|
||||
'warranty_months' => 'warranty',
|
||||
'full_name' => 'full name',
|
||||
'email' => 'email',
|
||||
'username' => 'username'
|
||||
'username' => 'username',
|
||||
'jobtitle' => 'job title',
|
||||
'employee_num' => 'employee number',
|
||||
'phone_number' => 'phone number',
|
||||
'first_name' => 'first name',
|
||||
'last_name' => 'last name',
|
||||
'department' => 'department',
|
||||
'manager_first_name' => 'manager first name',
|
||||
'manager_last_name' => 'manager last name',
|
||||
];
|
||||
/**
|
||||
* Map of item fields->csv names
|
||||
@@ -89,11 +102,11 @@ abstract class Importer
|
||||
public function __construct($file)
|
||||
{
|
||||
$this->fieldMap = $this->defaultFieldMap;
|
||||
// By default the importer passes a url to the file.
|
||||
// However, for testing we also support passing a string directly
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
// By default the importer passes a url to the file.
|
||||
// However, for testing we also support passing a string directly
|
||||
if (is_file($file)) {
|
||||
$this->csv = Reader::createFromPath($file);
|
||||
} else {
|
||||
@@ -109,24 +122,7 @@ abstract class Importer
|
||||
$headerRow = $this->csv->fetchOne();
|
||||
$results = $this->normalizeInputArray($this->csv->fetchAssoc());
|
||||
|
||||
// Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/
|
||||
// This 'inverts' the fields such that we have a collection of fields indexed by name.
|
||||
$cFs = CustomField::All();
|
||||
$this->customFields = $cFs->reduce(function ($nameLookup, $field) {
|
||||
$nameLookup[$field['name']] = $field;
|
||||
return $nameLookup;
|
||||
});
|
||||
// Remove any custom fields that do not exist in the header row. This prevents nulling out values that shouldn't exist.
|
||||
// In detail, we compare the lower case name of custom fields (indexed by name) to the keys in the header row. This
|
||||
// results in an array with only custom fields that are in the file.
|
||||
if ($this->customFields) {
|
||||
$this->customFields = array_intersect_key(
|
||||
array_change_key_case($this->customFields),
|
||||
array_change_key_case(array_flip($headerRow))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$this->populateCustomFields($headerRow);
|
||||
|
||||
DB::transaction(function () use (&$results) {
|
||||
Model::unguard();
|
||||
@@ -142,8 +138,35 @@ abstract class Importer
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
abstract protected function handle($row);
|
||||
|
||||
/**
|
||||
* Fetch custom fields from database and translate/parse them into a format
|
||||
* appropriate for use in the importer.
|
||||
* @return void
|
||||
* @author Daniel Meltzer
|
||||
* @since 5.0
|
||||
*/
|
||||
protected function populateCustomFields($headerRow)
|
||||
{
|
||||
// Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/
|
||||
// This 'inverts' the fields such that we have a collection of fields indexed by name.
|
||||
$this->customFields = CustomField::All()->reduce(function ($nameLookup, $field) {
|
||||
$nameLookup[$field['name']] = $field;
|
||||
return $nameLookup;
|
||||
});
|
||||
// Remove any custom fields that do not exist in the header row. This prevents nulling out values that shouldn't exist.
|
||||
// In detail, we compare the lower case name of custom fields (indexed by name) to the keys in the header row. This
|
||||
// results in an array with only custom fields that are in the file.
|
||||
if ($this->customFields) {
|
||||
$this->customFields = array_intersect_key(
|
||||
array_change_key_case($this->customFields),
|
||||
array_change_key_case(array_flip($headerRow))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Check to see if the given key exists in the array, and trim excess white space before returning it
|
||||
*
|
||||
@@ -158,10 +181,9 @@ abstract class Importer
|
||||
{
|
||||
|
||||
$val = $default;
|
||||
|
||||
$key = $this->lookupCustomKey($key);
|
||||
|
||||
$this->log("Custom Key: ${key}");
|
||||
// $this->log("Custom Key: ${key}");
|
||||
if (array_key_exists($key, $array)) {
|
||||
$val = Encoding::toUTF8(trim($array[ $key ]));
|
||||
}
|
||||
@@ -179,9 +201,7 @@ abstract class Importer
|
||||
*/
|
||||
public function lookupCustomKey($key)
|
||||
{
|
||||
// dd($this->fieldMap);
|
||||
if (array_key_exists($key, $this->fieldMap)) {
|
||||
$this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
|
||||
return $this->fieldMap[$key];
|
||||
}
|
||||
// Otherwise no custom key, return original.
|
||||
@@ -189,6 +209,8 @@ abstract class Importer
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to lowercase header values to ensure we're comparing values properly.
|
||||
*
|
||||
* @param $results
|
||||
* @return array
|
||||
*/
|
||||
@@ -229,88 +251,103 @@ abstract class Importer
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the user matching given data, or creates a new one if there is no match
|
||||
* Finds the user matching given data, or creates a new one if there is no match.
|
||||
* This is NOT used by the User Import, only for Asset/Accessory/etc where
|
||||
* there are users listed and we have to create them and associate them at
|
||||
* the same time. [ALG]
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @since 3.0
|
||||
* @param $row array
|
||||
* @return User Model w/ matching name
|
||||
* @internal param string $user_username Username extracted from CSV
|
||||
* @internal param string $user_email Email extracted from CSV
|
||||
* @internal param string $first_name
|
||||
* @internal param string $last_name
|
||||
* @internal param array $user_array User details parsed from csv
|
||||
*/
|
||||
protected function createOrFetchUser($row)
|
||||
{
|
||||
$user_name = $this->findCsvMatch($row, "full_name");
|
||||
$user_email = $this->findCsvMatch($row, "email");
|
||||
$user_username = $this->findCsvMatch($row, "username");
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
if(empty($user_name) && empty($user_email) && empty($user_username)) {
|
||||
$this->log('No user data provided - skipping user creation, just adding asset');
|
||||
//$user_username = '';
|
||||
$user_array = [
|
||||
'full_name' => $this->findCsvMatch($row, "full_name"),
|
||||
'email' => $this->findCsvMatch($row, "email"),
|
||||
'manager_id'=> '',
|
||||
'department_id' => '',
|
||||
'username' => $this->findCsvMatch($row, "username"),
|
||||
'activated' => $this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')),
|
||||
];
|
||||
\Log::debug('Importer.php Activated: '.$this->findCsvMatch($row, 'activated'));
|
||||
|
||||
// If the full name is empty, bail out--we need this to extract first name (at the very least)
|
||||
if(empty($user_array['full_name'])) {
|
||||
$this->log('Insufficient user data provided (Full name is required)- skipping user creation, just adding asset');
|
||||
return false;
|
||||
}
|
||||
// A username was given.
|
||||
if( !empty($user_username)) {
|
||||
$user = User::where('username', $user_username)->first();
|
||||
if($user) {
|
||||
return $user;
|
||||
|
||||
// Is the user actually an ID?
|
||||
if($user = $this->findUserByNumber($user_array['full_name'])) {
|
||||
return $user;
|
||||
}
|
||||
$this->log('User does not appear to be an id with number: '.$user_array['full_name'].'. Continuing through our processes');
|
||||
|
||||
// Populate email if it does not exist.
|
||||
if(empty($user_array['email'])) {
|
||||
$user_array['email'] = User::generateEmailFromFullName($user_array['full_name']);
|
||||
}
|
||||
|
||||
$user_formatted_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_array['full_name']);
|
||||
$user_array['first_name'] = $user_formatted_array['first_name'];
|
||||
$user_array['last_name'] = $user_formatted_array['last_name'];
|
||||
|
||||
if (empty($user_array['username'])) {
|
||||
$user_array['username'] = $user_formatted_array['username'];
|
||||
if ($this->usernameFormat =='email') {
|
||||
$user_array['username'] = $user_array['email'];
|
||||
}
|
||||
}
|
||||
|
||||
// Does this ever actually fire??
|
||||
// Check for a matching user after trying to guess username.
|
||||
if ($user = User::where('username', $user_array['username'])->first()) {
|
||||
$this->log('User '.$user_array['username'].' already exists');
|
||||
return $user;
|
||||
}
|
||||
|
||||
// If at this point we have not found a username or first name, bail out in shame.
|
||||
if(empty($user_array['username']) || empty($user_array['first_name'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No Luck, let's create one.
|
||||
$user = new User;
|
||||
$user->first_name = $user_array['first_name'];
|
||||
$user->last_name = $user_array['last_name'];
|
||||
$user->username = $user_array['username'];
|
||||
$user->email = $user_array['email'];
|
||||
$user->manager_id = (isset($user_array['manager_id']) ? $user_array['manager_id'] : null);
|
||||
$user->department_id = (isset($user_array['department_id']) ? $user_array['department_id']: null);
|
||||
$user->activated = $user_array['activated'];
|
||||
$user->password = $this->tempPassword;
|
||||
|
||||
\Log::debug('Creating a user with the following attributes: '.print_r($user_array, true));
|
||||
|
||||
if ($user->save()) {
|
||||
\Log::debug('Importer.php Name: '.$user->first_name.' '.$user->last_name.' ('.$user->username.')');
|
||||
$this->log('User '.$user_array['username'].' created');
|
||||
return $user;
|
||||
}
|
||||
$this->logError($user, 'User "' . $user_array['username'] . '" was not able to be created.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a user by user_id if user_name provided is a number
|
||||
* @param string $user_name users full name from csv
|
||||
* @return User User Matching ID
|
||||
*/
|
||||
protected function findUserByNumber($user_name)
|
||||
{
|
||||
// A number was given instead of a name
|
||||
if (is_numeric($user_name)) {
|
||||
$this->log('User '.$user_name.' is not a name - assume this user already exists');
|
||||
$user = User::find($user_name);
|
||||
if($user) {
|
||||
return $user;
|
||||
}
|
||||
$this->log('User with id'.$user_name.' does not exist. Continuing through our processes');
|
||||
$this->log('User '.$user_name.' is a number - lets see if it matches a user id');
|
||||
return User::find($user_name);
|
||||
}
|
||||
// Generate data based on user name.
|
||||
$user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
|
||||
$first_name = $user_email_array['first_name'];
|
||||
$last_name = $user_email_array['last_name'];
|
||||
|
||||
if (empty($user_email)) {
|
||||
if (Setting::getSettings()->email_domain) {
|
||||
$user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($user_username)) {
|
||||
if ($this->usernameFormat =='email') {
|
||||
$user_username = $user_email;
|
||||
} else {
|
||||
$user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
|
||||
$user_username = $user_name_array['username'];
|
||||
}
|
||||
}
|
||||
$user = new User;
|
||||
|
||||
if (!empty($user_username)) {
|
||||
|
||||
if ($user = User::MatchEmailOrUsername($user_username, $user_email)
|
||||
->whereNotNull('username')->first()) {
|
||||
$this->log('User '.$user_username.' already exists');
|
||||
} elseif (( $first_name != '') && ($last_name != '') && ($user_username != '')) {
|
||||
$user = new User;
|
||||
$user->first_name = $first_name;
|
||||
$user->last_name = $last_name;
|
||||
$user->username = $user_username;
|
||||
$user->email = $user_email;
|
||||
$user->activated = 1;
|
||||
$user->password = $this->tempPassword;
|
||||
|
||||
if ($user->save()) {
|
||||
$this->log('User '.$first_name.' created');
|
||||
} else {
|
||||
$this->logError($user, 'User "' . $first_name . '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,6 +378,20 @@ abstract class Importer
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Are we updating items in the import.
|
||||
*
|
||||
* @param bool $updating the updating
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setShouldNotify($send_welcome)
|
||||
{
|
||||
$this->send_welcome = $send_welcome;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines mappings of csv fields
|
||||
*
|
||||
@@ -388,4 +439,64 @@ abstract class Importer
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fetchHumanBoolean($value)
|
||||
{
|
||||
if (($value =='1') || (strtolower($value) =='true') || (strtolower($value) =='yes'))
|
||||
{
|
||||
return '1';
|
||||
}
|
||||
return '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an existing department, or create new if it doesn't exist
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 4.6.5
|
||||
* @param $user_department string
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function createOrFetchDepartment($user_department_name)
|
||||
{
|
||||
if ($user_department_name!='') {
|
||||
$department = Department::where('name', '=', $user_department_name)->first();
|
||||
|
||||
if ($department) {
|
||||
$this->log('A matching Department ' . $user_department_name . ' already exists');
|
||||
return $department->id;
|
||||
}
|
||||
|
||||
$department = new Department();
|
||||
$department->name = $user_department_name;
|
||||
|
||||
if ($department->save()) {
|
||||
$this->log('Department ' . $user_department_name . ' was created');
|
||||
return $department->id;
|
||||
}
|
||||
$this->logError($department, 'Department');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an existing manager
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 4.6.5
|
||||
* @param $user_manager string
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function fetchManager($user_manager_first_name, $user_manager_last_name)
|
||||
{
|
||||
$manager = User::where('first_name', '=', $user_manager_first_name)
|
||||
->where('last_name', '=', $user_manager_last_name)->first();
|
||||
if ($manager) {
|
||||
$this->log('A matching Manager ' . $user_manager_first_name . ' '. $user_manager_last_name . ' already exists');
|
||||
return $manager->id;
|
||||
}
|
||||
$this->log('No matching Manager ' . $user_manager_first_name . ' '. $user_manager_last_name . ' found. If their user account is being created through this import, you should re-process this file again. ');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
|
||||
class ItemImporter extends Importer
|
||||
@@ -54,6 +55,18 @@ class ItemImporter extends Importer
|
||||
if ($this->shouldUpdateField($item_supplier)) {
|
||||
$this->item['supplier_id'] = $this->createOrFetchSupplier($item_supplier);
|
||||
}
|
||||
|
||||
$item_department = $this->findCsvMatch($row, "department");
|
||||
if ($this->shouldUpdateField($item_department)) {
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($item_department);
|
||||
}
|
||||
|
||||
$item_manager_first_name = $this->findCsvMatch($row, "manager_first_name");
|
||||
$item_manager_last_name = $this->findCsvMatch($row, "manager_last_name");
|
||||
if ($this->shouldUpdateField($item_manager_first_name)) {
|
||||
$this->item['manager_id'] = $this->fetchManager($item_manager_first_name, $item_manager_last_name);
|
||||
}
|
||||
|
||||
$this->item["name"] = $this->findCsvMatch($row, "item_name");
|
||||
$this->item["notes"] = $this->findCsvMatch($row, "notes");
|
||||
$this->item["order_number"] = $this->findCsvMatch($row, "order_number");
|
||||
@@ -69,11 +82,34 @@ class ItemImporter extends Importer
|
||||
$this->item['serial'] = $this->findCsvMatch($row, "serial");
|
||||
// NO need to call this method if we're running the user import.
|
||||
// TODO: Merge these methods.
|
||||
$this->item['checkout_class'] = $this->findCsvMatch($row, "checkout_class");
|
||||
if(get_class($this) !== UserImporter::class) {
|
||||
$this->item["user"] = $this->createOrFetchUser($row);
|
||||
// $this->item["user"] = $this->createOrFetchUser($row);
|
||||
$this->item["checkout_target"] = $this->determineCheckout($row);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse row to determine what (if anything) we should checkout to.
|
||||
* @param array $row CSV Row being parsed
|
||||
* @return SnipeModel Model to be checked out to
|
||||
*/
|
||||
protected function determineCheckout($row)
|
||||
{
|
||||
// We only support checkout-to-location for asset, so short circuit otherwise.
|
||||
if(get_class($this) != AssetImporter::class) {
|
||||
return $this->createOrFetchUser($row);
|
||||
}
|
||||
|
||||
if ($this->item['checkout_class'] === 'location') {
|
||||
return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location')));
|
||||
}
|
||||
|
||||
return $this->createOrFetchUser($row);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the $item array before storing.
|
||||
* We need to remove any values that are not part of the fillable fields.
|
||||
@@ -138,7 +174,7 @@ class ItemImporter extends Importer
|
||||
* @since 3.0
|
||||
* @param array
|
||||
* @param $category Category
|
||||
* @param $manufacturer Manufacturer
|
||||
* @param $row Manufacturer
|
||||
* @return int Id of asset model created/found
|
||||
* @internal param $asset_modelno string
|
||||
*/
|
||||
@@ -256,6 +292,8 @@ class ItemImporter extends Importer
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the existing status label or create new if it doesn't exist.
|
||||
*
|
||||
|
||||
@@ -71,11 +71,11 @@ class LicenseImporter extends ItemImporter
|
||||
|
||||
// Lets try to checkout seats if the fields exist and we have seats.
|
||||
if ($license->seats > 0) {
|
||||
$user = $this->item['user'];
|
||||
$checkout_target = $this->item['checkout_target'];
|
||||
$asset = Asset::where('asset_tag', $asset_tag)->first();
|
||||
$targetLicense = $license->licenseSeats()->first();
|
||||
if ($user) {
|
||||
$targetLicense->assigned_to = $user->id;
|
||||
if ($checkout_target) {
|
||||
$targetLicense->assigned_to = $checkout_target->id;
|
||||
if ($asset) {
|
||||
$targetLicense->asset_id = $asset->id;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,23 @@ namespace App\Importer;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\User;
|
||||
use App\Notifications\WelcomeNotification;
|
||||
|
||||
/**
|
||||
* This is ONLY used for the User Import. When we are importing users
|
||||
* via an Asset/etc import, we use createOrFetchUser() in
|
||||
* App\Importer.php. [ALG]
|
||||
*
|
||||
* Class UserImporter
|
||||
* @package App\Importer
|
||||
*
|
||||
*/
|
||||
class UserImporter extends ItemImporter
|
||||
{
|
||||
protected $users;
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
// $this->users = User::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
@@ -26,41 +35,74 @@ class UserImporter extends ItemImporter
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @since 4.0
|
||||
* @param array $row
|
||||
*/
|
||||
public function createUserIfNotExists(array $row)
|
||||
{
|
||||
// User Specific Bits
|
||||
// Pull the records from the CSV to determine their values
|
||||
$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');
|
||||
\Log::debug('UserImporter.php Name: '.$this->item['first_name'].' '.$this->item['last_name'].' ('.$this->item['username'].')');
|
||||
$this->item['email'] = $this->findCsvMatch($row, 'email');
|
||||
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
|
||||
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
|
||||
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
|
||||
|
||||
\Log::debug('UserImporter.php Activated: '.$this->findCsvMatch($row, 'activated'));
|
||||
\Log::debug('UserImporter.php Activated fetchHumanBoolean: '. $this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')));
|
||||
|
||||
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
|
||||
$this->item['password'] = $this->tempPassword;
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department')) ? $this->createOrFetchDepartment($this->findCsvMatch($row, 'department')) : null;
|
||||
$this->item['manager_id'] = $this->fetchManager($this->findCsvMatch($row, 'manager_first_name'), $this->findCsvMatch($row, 'manager_last_name')) ? $this->fetchManager($this->findCsvMatch($row, 'manager_first_name'), $this->findCsvMatch($row, 'manager_last_name')) : null;
|
||||
|
||||
|
||||
$user = User::where('username', $this->item['username'])->first();
|
||||
if ($user) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching User ' . $this->item["name"] . ' already exists. ');
|
||||
\Log::debug('A matching User ' . $this->item["name"] . ' already exists. ');
|
||||
return;
|
||||
}
|
||||
$this->log('Updating User');
|
||||
// $user = $this->users[$userId];
|
||||
$user->update($this->sanitizeItemForUpdating($user));
|
||||
$user->save();
|
||||
// \Log::debug('UserImporter.php Updated User ' . print_r($user, true));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords
|
||||
// Issue #5408
|
||||
$this->item['password'] = $this->tempPassword;
|
||||
|
||||
$this->log("No matching user, creating one");
|
||||
$user = new User();
|
||||
$user->fill($this->sanitizeItemForStoring($user));
|
||||
|
||||
if ($user->save()) {
|
||||
// $user->logCreate('Imported using CSV Importer');
|
||||
$this->log("User " . $this->item["name"] . ' was created');
|
||||
|
||||
if(($user->email) && ($user->activated=='1')) {
|
||||
$data = [
|
||||
'email' => $user->email,
|
||||
'username' => $user->username,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'password' => $this->tempPassword,
|
||||
];
|
||||
|
||||
if ($this->send_welcome) {
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
}
|
||||
$user = null;
|
||||
$this->item = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logError($user, 'User');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
| CSV | Item | Applicable Types |
|
||||
|---------------------|------------------|-------------------------------------------|
|
||||
| activated | | User |
|
||||
| asset tag | asset_tag | Asset |
|
||||
| category | category | All |
|
||||
| company | company | All |
|
||||
| department_id | | User ? All |
|
||||
| item name | item_name | All |
|
||||
| image | image | asset |
|
||||
| image | image | Asset |
|
||||
| email | | |
|
||||
| expiration date | expiration_date | License |
|
||||
| location | location | All |
|
||||
| notes | notes | All |
|
||||
| licensed to email | license_email | License |
|
||||
| licensed to name | license_name | License |
|
||||
| maintained | maintained | License |
|
||||
| manager_id | | User |
|
||||
| manufacturer | manufacturer | All |
|
||||
| model name | asset_model | Asset |
|
||||
| model number | model_number | Asset |
|
||||
@@ -22,12 +26,12 @@
|
||||
| reassignable | reassignable | License |
|
||||
| requestable | requestable | Asset, Accessory? |
|
||||
| seats | seats | License |
|
||||
| serial number | serial | asset, license |
|
||||
| status | status | asset ? All |
|
||||
| serial number | serial | Asset, license |
|
||||
| status | status | Asset ? All |
|
||||
| supplier | supplier | Asset ? All |
|
||||
| termination date | termination_date | License |
|
||||
| warranty months | warranty_months | asset |
|
||||
| warranty months | warranty_months | Asset |
|
||||
| User Related Fields | assigned_to | Asset |
|
||||
| name | | |
|
||||
| email | | |
|
||||
| username | | |
|
||||
| username | | |
|
||||
|
||||
|
||||
46
app/Listeners/LogFailedLogin.php
Normal file
46
app/Listeners/LogFailedLogin.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Auth\Events\Failed;
|
||||
use Illuminate\Http\Request;
|
||||
use DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class LogFailedLogin
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \Illuminate\Auth\Events\Failed $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Failed $event)
|
||||
{
|
||||
$now = new Carbon();
|
||||
try {
|
||||
DB::table('login_attempts')->insert(
|
||||
[
|
||||
'username' => $event->credentials['username'],
|
||||
'user_agent' => request()->header('User-Agent'),
|
||||
'remote_ip' => request()->ip(),
|
||||
'successful' => 0,
|
||||
'created_at' => $now,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
49
app/Listeners/LogSuccessfulLogin.php
Normal file
49
app/Listeners/LogSuccessfulLogin.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Illuminate\Http\Request;
|
||||
use DB;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class LogSuccessfulLogin
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param Login $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Login $event)
|
||||
{
|
||||
$now = new Carbon();
|
||||
|
||||
try {
|
||||
|
||||
DB::table('login_attempts')->insert(
|
||||
[
|
||||
'username' => $event->user->username,
|
||||
'user_agent' => request()->header('User-Agent'),
|
||||
'remote_ip' => request()->ip(),
|
||||
'successful' => 1,
|
||||
'created_at' => $now,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug($e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
@@ -25,6 +26,28 @@ class Accessory extends SnipeModel
|
||||
'requestable' => 'boolean'
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'category' => ['name'],
|
||||
'company' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'location' => ['name']
|
||||
];
|
||||
|
||||
/**
|
||||
* Set static properties to determine which checkout/checkin handlers we should use
|
||||
*/
|
||||
@@ -171,40 +194,6 @@ class Accessory extends SnipeModel
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
$query->whereHas('category', function ($query) use ($search) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search.'%');
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('company', function ($query) use ($search) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('manufacturer', function ($query) use ($search) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('location', function ($query) use ($search) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere('accessories.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('accessories.model_number', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('accessories.order_number', 'LIKE', '%'.$search.'%');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -24,6 +25,24 @@ class Actionlog extends SnipeModel
|
||||
public $timestamps = true;
|
||||
protected $fillable = [ 'created_at', 'item_type','user_id','item_id','action_type','note','target_id', 'target_type' ];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['action_type', 'note', 'log_meta'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'company' => ['name']
|
||||
];
|
||||
|
||||
// Overridden from Builder to automatically add the company
|
||||
public static function boot()
|
||||
{
|
||||
@@ -200,31 +219,4 @@ class Actionlog extends SnipeModel
|
||||
->orderBy('created_at', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text for complex Bootstrap Tables API
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
$search = explode(' OR ', $search);
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
foreach ($search as $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->whereHas('company', function ($query) use ($search) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere('action_type', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('note', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('log_meta', 'LIKE', '%'.$search.'%');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Http\Traits\UniqueSerialTrait;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use AssetPresenter;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Log;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
@@ -23,12 +26,13 @@ use App\Notifications\CheckoutAssetNotification;
|
||||
class Asset extends Depreciable
|
||||
{
|
||||
protected $presenter = 'App\Presenters\AssetPresenter';
|
||||
use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||
use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait;
|
||||
|
||||
const LOCATION = 'location';
|
||||
const ASSET = 'asset';
|
||||
const USER = 'user';
|
||||
|
||||
const ACCEPTANCE_PENDING = 'pending';
|
||||
/**
|
||||
* Set static properties to determine which checkout/checkin handlers we should use
|
||||
*/
|
||||
@@ -72,12 +76,13 @@ class Asset extends Depreciable
|
||||
'status_id' => 'required|integer|exists:status_labels,id',
|
||||
'company_id' => 'integer|nullable',
|
||||
'warranty_months' => 'numeric|nullable',
|
||||
'physical' => 'numeric|max:1|nullable',
|
||||
'physical' => 'numeric|max:1|nullable',
|
||||
'checkout_date' => 'date|max:10|min:10|nullable',
|
||||
'checkin_date' => 'date|max:10|min:10|nullable',
|
||||
'supplier_id' => 'numeric|nullable',
|
||||
'asset_tag' => 'required|min:1|max:255|unique_undeleted',
|
||||
'status' => 'integer',
|
||||
'serial' => 'unique_serial|nullable',
|
||||
'purchase_cost' => 'numeric|nullable',
|
||||
'next_audit_date' => 'date|nullable',
|
||||
'last_audit_date' => 'date|nullable',
|
||||
@@ -108,7 +113,42 @@ class Asset extends Depreciable
|
||||
'warranty_months',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'asset_tag',
|
||||
'serial',
|
||||
'order_number',
|
||||
'purchase_cost',
|
||||
'notes',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'purchase_date',
|
||||
'expected_checkin',
|
||||
'next_audit_date',
|
||||
'last_audit_date'
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'assetstatus' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'company' => ['name'],
|
||||
'defaultLoc' => ['name'],
|
||||
'model' => ['name', 'model_number'],
|
||||
'model.category' => ['name'],
|
||||
'model.manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
public function getDisplayNameAttribute()
|
||||
{
|
||||
@@ -188,13 +228,21 @@ class Asset extends Depreciable
|
||||
if($target->location) {
|
||||
$this->location_id = $target->location->id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->requireAcceptance()) {
|
||||
if(get_class($target) != User::class) {
|
||||
throw new CheckoutNotAllowed;
|
||||
if($target instanceof Location) {
|
||||
$this->location_id = $target->id;
|
||||
}
|
||||
$this->accepted="pending";
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have to confirm that they accept the asset?
|
||||
*
|
||||
* If so, set the acceptance-status to "pending".
|
||||
* This value is used in the unaccepted assets reports, for example
|
||||
*
|
||||
* @see https://github.com/snipe/snipe-it/issues/5772
|
||||
*/
|
||||
if ($this->requireAcceptance() && $target instanceof User) {
|
||||
$this->accepted = self::ACCEPTANCE_PENDING;
|
||||
}
|
||||
|
||||
if ($this->save()) {
|
||||
@@ -572,6 +620,64 @@ class Asset extends Depreciable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $terms The search terms
|
||||
* @return Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function advancedTextSearch(Builder $query, array $terms) {
|
||||
|
||||
|
||||
/**
|
||||
* Assigned user
|
||||
*/
|
||||
$query = $query->leftJoin('users as assets_users',function ($leftJoin) {
|
||||
$leftJoin->on("assets_users.id", "=", "assets.assigned_to")
|
||||
->where("assets.assigned_type", "=", User::class);
|
||||
});
|
||||
|
||||
foreach($terms as $term) {
|
||||
|
||||
$query = $query
|
||||
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%", "%$term%"]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigned location
|
||||
*/
|
||||
$query = $query->leftJoin('locations as assets_locations',function ($leftJoin) {
|
||||
$leftJoin->on("assets_locations.id","=","assets.assigned_to")
|
||||
->where("assets.assigned_type","=",Location::class);
|
||||
});
|
||||
|
||||
foreach($terms as $term) {
|
||||
|
||||
$query = $query->orWhere('assets_locations.name', 'LIKE', '%'.$term.'%');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigned assets
|
||||
*/
|
||||
$query = $query->leftJoin('assets as assigned_assets',function ($leftJoin) {
|
||||
$leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
|
||||
->where('assets.assigned_type', '=', Asset::class);
|
||||
});
|
||||
|
||||
foreach($terms as $term) {
|
||||
|
||||
$query = $query->orWhere('assigned_assets.name', 'LIKE', '%'.$term.'%');
|
||||
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
@@ -805,83 +911,6 @@ class Asset extends Depreciable
|
||||
return $query->where("accepted", "=", "accepted");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text for complex Bootstrap Tables API.
|
||||
* This is really horrible, but I can't think of a less-awful way to do it.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
$search = explode(' OR ', $search);
|
||||
|
||||
return $query->leftJoin('users as assets_users',function ($leftJoin) {
|
||||
$leftJoin->on("assets_users.id", "=", "assets.assigned_to")
|
||||
->where("assets.assigned_type", "=", User::class);
|
||||
})->leftJoin('locations as assets_locations',function ($leftJoin) {
|
||||
$leftJoin->on("assets_locations.id","=","assets.assigned_to")
|
||||
->where("assets.assigned_type","=",Location::class);
|
||||
})->leftJoin('assets as assigned_assets',function ($leftJoin) {
|
||||
$leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
|
||||
->where('assets.assigned_type', '=', Asset::class);
|
||||
})->where(function ($query) use ($search) {
|
||||
foreach ($search as $search) {
|
||||
$query->whereHas('model', function ($query) use ($search) {
|
||||
$query->whereHas('category', function ($query) use ($search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('models.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('models.model_number', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
});
|
||||
})->orWhereHas('model', function ($query) use ($search) {
|
||||
$query->whereHas('manufacturer', function ($query) use ($search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
});
|
||||
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('assetstatus', function ($query) use ($search) {
|
||||
$query->where('status_labels.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('supplier', function ($query) use ($search) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('company', function ($query) use ($search) {
|
||||
$query->where('companies.name', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('defaultLoc', function ($query) use ($search) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%", "%$search%"])
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
|
||||
})->orWhere('assets.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets.asset_tag', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets.serial', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets.order_number', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets.purchase_cost', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets.notes', 'LIKE', '%'.$search.'%');
|
||||
}
|
||||
foreach (CustomField::all() as $field) {
|
||||
$query->orWhere('assets.'.$field->db_column_name(), 'LIKE', "%$search%");
|
||||
}
|
||||
})->withTrashed()->whereNull("assets.deleted_at"); //workaround for laravel bug
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text for complex Bootstrap Tables API.
|
||||
*
|
||||
@@ -1151,8 +1180,8 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function scopeInCategory($query, $category_id)
|
||||
{
|
||||
return $query->join('models', 'assets.model_id', '=', 'models.id')
|
||||
->join('categories', 'models.category_id', '=', 'categories.id')->where('models.category_id', '=', $category_id);
|
||||
return $query->join('models as category_models', 'assets.model_id', '=', 'category_models.id')
|
||||
->join('categories', 'category_models.category_id', '=', 'categories.id')->where('category_models.category_id', '=', $category_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\Searchable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -19,7 +20,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
use ValidatingTrait;
|
||||
|
||||
|
||||
protected $dates = [ 'deleted_at' ];
|
||||
protected $dates = [ 'deleted_at', 'start_date' , 'completion_date'];
|
||||
protected $table = 'asset_maintenances';
|
||||
// Declaring rules for form validation
|
||||
protected $rules = [
|
||||
@@ -28,12 +29,29 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
'asset_maintenance_type' => 'required',
|
||||
'title' => 'required|max:100',
|
||||
'is_warranty' => 'boolean',
|
||||
'start_date' => 'required|date_format:"Y-m-d"',
|
||||
'completion_date' => 'nullable|date_format:"Y-m-d"',
|
||||
'start_date' => 'required|date',
|
||||
'completion_date' => 'nullable|date',
|
||||
'notes' => 'string|nullable',
|
||||
'cost' => 'numeric|nullable'
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['title', 'notes', 'asset_maintenance_type', 'cost', 'start_date', 'completion_date'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
|
||||
public function getCompanyableParents()
|
||||
{
|
||||
return [ 'asset' ];
|
||||
@@ -54,6 +72,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
trans('admin/asset_maintenances/general.repair') => trans('admin/asset_maintenances/general.repair'),
|
||||
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade'),
|
||||
'PAT test' => 'PAT test',
|
||||
trans('admin/asset_maintenances/general.calibration') => trans('admin/asset_maintenances/general.calibration'),
|
||||
'PAT test' => 'PAT test',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -139,30 +159,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
$query->where('asset_maintenances.title', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('asset_maintenances.notes', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('asset_maintenances.asset_maintenance_type', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('asset_maintenances.cost', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('asset_maintenances.start_date', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('asset_maintenances.completion_date', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on admin user
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Requestable;
|
||||
use App\Models\SnipeModel;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -68,6 +69,26 @@ class AssetModel extends SnipeModel
|
||||
'user_id',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'model_number', 'notes', 'eol'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'depreciation' => ['name'],
|
||||
'category' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
public function assets()
|
||||
{
|
||||
return $this->hasMany('\App\Models\Asset', 'model_id');
|
||||
@@ -153,45 +174,14 @@ class AssetModel extends SnipeModel
|
||||
* @param $query
|
||||
*
|
||||
* @return $query
|
||||
* @author Daniel Meltzer <parallelgrapefruit@gmail.com
|
||||
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
||||
* @version v3.5
|
||||
*/
|
||||
public function scopeRequestableModels($query)
|
||||
{
|
||||
|
||||
return $query->where('requestable', '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where('models.name', 'LIKE', "%$search%")
|
||||
->orWhere('model_number', 'LIKE', "%$search%")
|
||||
->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('depreciation', function ($query) use ($search) {
|
||||
$query->where('depreciations.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})
|
||||
->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('category', function ($query) use ($search) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})
|
||||
->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('manufacturer', function ($query) use ($search) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text, including catgeory and manufacturer name
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\SnipeModel;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -63,6 +64,21 @@ class Category extends SnipeModel
|
||||
'user_id',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'category_type'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
public function has_models()
|
||||
{
|
||||
@@ -143,22 +159,4 @@ class Category extends SnipeModel
|
||||
|
||||
return $query->where('require_acceptance', '=', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
$query->where('name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('category_type', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\SnipeModel;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Auth;
|
||||
use DB;
|
||||
@@ -35,6 +36,21 @@ final class Company extends SnipeModel
|
||||
protected $injectUniqueIdentifier = true;
|
||||
use ValidatingTrait;
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -192,20 +208,4 @@ final class Company extends SnipeModel
|
||||
{
|
||||
return $this->hasMany(Component::class, 'company_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
$query->where('name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
@@ -67,6 +68,26 @@ class Component extends SnipeModel
|
||||
'serial',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'category' => ['name'],
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
];
|
||||
|
||||
public function location()
|
||||
{
|
||||
return $this->belongsTo('\App\Models\Location', 'location_id');
|
||||
@@ -114,49 +135,7 @@ class Component extends SnipeModel
|
||||
$total = $this->qty;
|
||||
$remaining = $total - $checkedout;
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
$search = explode(' ', $search);
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
foreach ($search as $search) {
|
||||
$query->whereHas('category', function ($query) use ($search) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search.'%');
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('company', function ($query) use ($search) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('location', function ($query) use ($search) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere('components.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('components.order_number', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('components.serial', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('components.purchase_cost', 'LIKE', '%'.$search.'%');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
@@ -34,7 +35,7 @@ class Consumable extends SnipeModel
|
||||
'qty' => 'required|integer|min:0',
|
||||
'category_id' => 'required|integer',
|
||||
'company_id' => 'integer|nullable',
|
||||
'min_amt' => 'integer|min:1|nullable',
|
||||
'min_amt' => 'integer|min:0|nullable',
|
||||
'purchase_cost' => 'numeric|nullable',
|
||||
);
|
||||
|
||||
@@ -68,6 +69,27 @@ class Consumable extends SnipeModel
|
||||
'requestable'
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'category' => ['name'],
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
public function setRequestableAttribute($value)
|
||||
{
|
||||
if ($value == '') {
|
||||
@@ -134,6 +156,10 @@ class Consumable extends SnipeModel
|
||||
return $this->belongsToMany('\App\Models\User', 'consumables_users', 'consumable_id', 'assigned_to')->count();
|
||||
}
|
||||
|
||||
public function checkin_email()
|
||||
{
|
||||
return $this->category->checkin_email;
|
||||
}
|
||||
|
||||
public function requireAcceptance()
|
||||
{
|
||||
@@ -163,50 +189,6 @@ class Consumable extends SnipeModel
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
$search = explode(' ', $search);
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
foreach ($search as $search) {
|
||||
$query->whereHas('category', function ($query) use ($search) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search.'%');
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('company', function ($query) use ($search) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('location', function ($query) use ($search) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->whereHas('manufacturer', function ($query) use ($search) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
})->orWhere('consumables.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('consumables.order_number', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('consumables.purchase_cost', 'LIKE', '%'.$search.'%');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ class CustomField extends Model
|
||||
public $guarded=["id"];
|
||||
public static $PredefinedFormats=[
|
||||
"ANY" => "",
|
||||
"CUSTOM REGEX" => "",
|
||||
"ALPHA" => "alpha",
|
||||
"ALPHA-DASH" => "alpha_dash",
|
||||
"NUMERIC" => "numeric",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Searchable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Log;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
@@ -24,11 +25,11 @@ class Department extends SnipeModel
|
||||
use ValidatingTrait, UniqueUndeletedTrait;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|max:255',
|
||||
'user_id' => 'required',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
'name' => 'required|max:255',
|
||||
'user_id' => 'nullable|exists:users,id',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -45,6 +46,22 @@ class Department extends SnipeModel
|
||||
'notes',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'notes'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
|
||||
public function company()
|
||||
{
|
||||
@@ -76,23 +93,7 @@ class Department extends SnipeModel
|
||||
{
|
||||
return $this->belongsTo('\App\Models\Location', 'location_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextsearch($query, $search)
|
||||
{
|
||||
return $query->where('name', 'LIKE', "%$search%")
|
||||
->orWhere('notes', 'LIKE', "%$search%");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to order on location name
|
||||
*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
@@ -31,6 +32,21 @@ class Depreciation extends SnipeModel
|
||||
*/
|
||||
protected $fillable = ['name','months'];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'months'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
|
||||
public function has_models()
|
||||
@@ -41,23 +57,5 @@ class Depreciation extends SnipeModel
|
||||
public function has_licenses()
|
||||
{
|
||||
return $this->hasMany('\App\Models\License', 'depreciation_id')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
$query->where('name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('months', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\SnipeModel;
|
||||
use App\Models\Traits\Searchable;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
class Group extends SnipeModel
|
||||
@@ -22,6 +23,21 @@ class Group extends SnipeModel
|
||||
protected $injectUniqueIdentifier = true;
|
||||
use ValidatingTrait;
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'created_at'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
/**
|
||||
* Get user groups
|
||||
@@ -36,21 +52,4 @@ class Group extends SnipeModel
|
||||
{
|
||||
return json_decode($this->permissions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text
|
||||
*
|
||||
* @param Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $search Search term
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
|
||||
return $query->where(function ($query) use ($search) {
|
||||
|
||||
$query->where('name', 'LIKE', '%'.$search.'%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user