Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5c2d77c7d | ||
|
|
ce16eae508 | ||
|
|
dc73dbfbfd | ||
|
|
dae26e0378 | ||
|
|
1bb1f7342f | ||
|
|
420e8bc85a | ||
|
|
a521523d45 | ||
|
|
25884a893e | ||
|
|
d1e9fbfa24 | ||
|
|
da015ec4a8 | ||
|
|
1451b4f45d | ||
|
|
b6da68a69c | ||
|
|
dee92cfc6c | ||
|
|
dec77890bd | ||
|
|
0e1289f12f | ||
|
|
7b33f95e83 | ||
|
|
ab6744dfba | ||
|
|
0fd940ffa4 | ||
|
|
5893e25b43 | ||
|
|
7c3bbe3097 | ||
|
|
858d382e26 | ||
|
|
de16fee00a | ||
|
|
7deab0f53b | ||
|
|
e59ec8b27f | ||
|
|
6d98bd6846 | ||
|
|
58768e5aee | ||
|
|
28a450ea25 | ||
|
|
1393f44070 | ||
|
|
8016939f31 | ||
|
|
c1ad2f9376 | ||
|
|
9575cd2651 | ||
|
|
cf086b711e | ||
|
|
53db96edad | ||
|
|
3b62c4a83a | ||
|
|
5f3147cf36 | ||
|
|
738896bdc2 | ||
|
|
e2834fab90 | ||
|
|
d687e1d762 | ||
|
|
6256abddf2 | ||
|
|
b26fbf986f | ||
|
|
5c9b1ed43a | ||
|
|
14eb6b387b | ||
|
|
35ebe33e4e | ||
|
|
9035707bd6 | ||
|
|
aa1e06f021 | ||
|
|
30b1cfabf5 | ||
|
|
e75d22ab73 | ||
|
|
b1e17743b8 | ||
|
|
e2c0f01a10 | ||
|
|
f88fee0f21 | ||
|
|
c0669150fb | ||
|
|
f3c12f38b6 | ||
|
|
5e19178a30 | ||
|
|
90cddb7aee | ||
|
|
6d828964be | ||
|
|
971fcf5800 | ||
|
|
2ad270cf33 | ||
|
|
8ce78c6b31 | ||
|
|
af3c8195af | ||
|
|
117b4c59cc | ||
|
|
194d0733d4 | ||
|
|
a371e8d53f | ||
|
|
8f09cca043 | ||
|
|
39bca49e8f | ||
|
|
b8269020ae | ||
|
|
601c129bbf | ||
|
|
b293d00699 | ||
|
|
75a0cf97e2 | ||
|
|
c055e3af21 | ||
|
|
a1f93e733c | ||
|
|
49073742b5 | ||
|
|
187206cb88 | ||
|
|
8420cb7ec1 | ||
|
|
75252bce05 | ||
|
|
794824713e | ||
|
|
8f6ea84fca | ||
|
|
ea1b792a93 | ||
|
|
4ffb8f14b8 | ||
|
|
d023f61bc4 | ||
|
|
dd5ca73602 | ||
|
|
2632f730d1 | ||
|
|
24c158bfe6 | ||
|
|
3d4a5a8066 | ||
|
|
db7e0b56f2 | ||
|
|
f2478d813c | ||
|
|
192aa9eb71 | ||
|
|
0eef0fc1dd | ||
|
|
81f8fe34cd | ||
|
|
f744696043 | ||
|
|
29b0780c6c | ||
|
|
6b3b673daa | ||
|
|
a4876e9f3e | ||
|
|
925258bfb4 | ||
|
|
1a10aa0dda | ||
|
|
d6f8d1b464 | ||
|
|
09a102fea8 | ||
|
|
304fce73fc | ||
|
|
295a68bb7a | ||
|
|
3aeb521782 | ||
|
|
f587d2248b | ||
|
|
8579c5a68a | ||
|
|
835b461d7d | ||
|
|
b8a37a0c73 | ||
|
|
41b5b1dfd0 | ||
|
|
c7596e7741 | ||
|
|
d4fa81301d | ||
|
|
ec7245965f | ||
|
|
de76e8db5f | ||
|
|
a52575c7bf | ||
|
|
bf6703c2e8 | ||
|
|
4db1dd8afc | ||
|
|
c39e3acb59 | ||
|
|
7a44da85a0 | ||
|
|
890b613f71 | ||
|
|
1014bd74e0 | ||
|
|
db385e024b | ||
|
|
c8bff3ef38 | ||
|
|
10bc35d604 | ||
|
|
eea65a3f26 | ||
|
|
3b21a19491 | ||
|
|
7a52477294 | ||
|
|
ef0bd72076 | ||
|
|
ff824ec4db | ||
|
|
75032def9e | ||
|
|
3a0f738fb0 | ||
|
|
55846cc717 | ||
|
|
1784278a59 | ||
|
|
afac0bc441 | ||
|
|
ffbee77f6f | ||
|
|
b69b5fdf84 | ||
|
|
89e06054bf | ||
|
|
3159e7713a | ||
|
|
adf6e7d1cd | ||
|
|
ba3662a9ed | ||
|
|
05ea61421f | ||
|
|
22ef2ce0b6 | ||
|
|
51d3d130e4 |
@@ -1650,6 +1650,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timothyfarmer",
|
||||
"name": "Tim Farmer",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7632599?v=4",
|
||||
"profile": "https://github.com/timothyfarmer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -72,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
|
||||
|
||||
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# You can add one username per supported platform and one custom link
|
||||
# patreon: # Replace with your Patreon username
|
||||
# open_collective: # Replace with your Open Collective username
|
||||
# ko_fi: # Replace with your Ko-fi username
|
||||
custom: https://snipeitapp.com/donate
|
||||
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:
|
||||
|
||||
20
Dockerfile
20
Dockerfile
@@ -18,9 +18,8 @@ patch \
|
||||
curl \
|
||||
vim \
|
||||
git \
|
||||
cron \
|
||||
mysql-client \
|
||||
cron \
|
||||
supervisor \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
@@ -69,7 +68,9 @@ RUN \
|
||||
&& rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
|
||||
&& mkdir "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
|
||||
&& ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
|
||||
&& chown docker "/var/lib/snipeit/keys/"
|
||||
&& chown docker "/var/lib/snipeit/keys/" \
|
||||
&& chmod +x /var/www/html/artisan \
|
||||
&& echo "Finished setting up application in /var/www/html"
|
||||
|
||||
############## DEPENDENCIES via COMPOSER ###################
|
||||
|
||||
@@ -96,16 +97,11 @@ VOLUME ["/var/lib/snipeit"]
|
||||
|
||||
##### START SERVER
|
||||
|
||||
COPY docker/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
COPY docker/startup.sh docker/supervisord.conf /
|
||||
COPY docker/supervisor-exit-event-listener /usr/bin/supervisor-exit-event-listener
|
||||
RUN chmod +x /startup.sh /usr/bin/supervisor-exit-event-listener
|
||||
|
||||
# Add Tini
|
||||
ENV TINI_VERSION v0.14.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
|
||||
CMD ["/entrypoint.sh"]
|
||||
CMD ["/startup.sh"]
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
@@ -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/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)
|
||||
[](#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.
|
||||
|
||||
-----
|
||||
|
||||
@@ -59,6 +59,7 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
- [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
|
||||
- [Marksman](https://github.com/Scope-IT/marksman) - A Windows agent for Snipe-IT
|
||||
|
||||
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. :)
|
||||
|
||||
@@ -95,7 +96,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/982885?v=4" width="110px;"/><br /><sub>Martin Stub</sub>](http://martinstub.dk)<br />[🌍](#translation-stubben "Translation") | [<img src="https://avatars2.githubusercontent.com/u/28959963?v=4" width="110px;"/><br /><sub>Meyer Flavio</sub>](https://github.com/meyerf99)<br />[🌍](#translation-meyerf99 "Translation") | [<img src="https://avatars3.githubusercontent.com/u/796443?v=4" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[🌍](#translation-MicaelRodrigues "Translation") | [<img src="https://avatars0.githubusercontent.com/u/10481331?v=4" width="110px;"/><br /><sub>Mikael Rasmussen</sub>](http://rubixy.com/)<br />[🌍](#translation-mikaelssen "Translation") | [<img src="https://avatars1.githubusercontent.com/u/1544552?v=4" width="110px;"/><br /><sub>IxFail</sub>](https://github.com/IxFail)<br />[🌍](#translation-IxFail "Translation") | [<img src="https://avatars3.githubusercontent.com/u/18483118?v=4" width="110px;"/><br /><sub>Mohammed Fota</sub>](http://www.mohammedfota.com)<br />[🌍](#translation-MohammedFota "Translation") | [<img src="https://avatars0.githubusercontent.com/u/227080?v=4" width="110px;"/><br /><sub>Moayad Alserihi</sub>](https://github.com/omego)<br />[🌍](#translation-omego "Translation") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1680266?v=4" width="110px;"/><br /><sub>saymd</sub>](https://github.com/saymd)<br />[🌍](#translation-saymd "Translation") | [<img src="https://avatars0.githubusercontent.com/u/1826808?v=4" width="110px;"/><br /><sub>Patrik Larsson</sub>](https://nordsken.se)<br />[🌍](#translation-pooot "Translation") | [<img src="https://avatars1.githubusercontent.com/u/20584746?v=4" width="110px;"/><br /><sub>drcryo</sub>](https://github.com/drcryo)<br />[🌍](#translation-drcryo "Translation") | [<img src="https://avatars1.githubusercontent.com/u/19408004?v=4" width="110px;"/><br /><sub>pawel1615</sub>](https://github.com/pawel1615)<br />[🌍](#translation-pawel1615 "Translation") | [<img src="https://avatars2.githubusercontent.com/u/23340468?v=4" width="110px;"/><br /><sub>bodrovics</sub>](https://github.com/bodrovics)<br />[🌍](#translation-bodrovics "Translation") | [<img src="https://avatars0.githubusercontent.com/u/3257654?v=4" width="110px;"/><br /><sub>priatna</sub>](https://github.com/priatna)<br />[🌍](#translation-priatna "Translation") | [<img src="https://avatars1.githubusercontent.com/u/5358374?v=4" width="110px;"/><br /><sub>Fan Jiang</sub>](https://amayume.net)<br />[🌍](#translation-ProfFan "Translation") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/22555451?v=4" width="110px;"/><br /><sub>ragnarcx</sub>](https://github.com/ragnarcx)<br />[🌍](#translation-ragnarcx "Translation") | [<img src="https://avatars2.githubusercontent.com/u/18654582?v=4" width="110px;"/><br /><sub>Rein van Haaren</sub>](http://www.reinvanhaaren.nl/)<br />[🌍](#translation-reinvanhaaren "Translation") | [<img src="https://avatars1.githubusercontent.com/u/386672?v=4" width="110px;"/><br /><sub>Teguh Dwicaksana</sub>](http://dheche.songolimo.net)<br />[🌍](#translation-dheche "Translation") | [<img src="https://avatars2.githubusercontent.com/u/2572552?v=4" width="110px;"/><br /><sub>fraccie</sub>](https://github.com/FRaccie)<br />[🌍](#translation-FRaccie "Translation") | [<img src="https://avatars0.githubusercontent.com/u/35182720?v=4" width="110px;"/><br /><sub>vinzruzell</sub>](https://github.com/vinzruzell)<br />[🌍](#translation-vinzruzell "Translation") | [<img src="https://avatars1.githubusercontent.com/u/7883603?v=4" width="110px;"/><br /><sub>Kevin Austin</sub>](http://kevinaustin.com)<br />[🌍](#translation-vipsystem "Translation") | [<img src="https://avatars3.githubusercontent.com/u/3861828?v=4" width="110px;"/><br /><sub>Wira Sandy</sub>](http://azuraweb.xyz)<br />[🌍](#translation-wira-sandy "Translation") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") |
|
||||
| [<img src="https://avatars2.githubusercontent.com/u/8663789?v=4" width="110px;"/><br /><sub>Илья</sub>](https://github.com/GrayHoax)<br />[🌍](#translation-GrayHoax "Translation") | [<img src="https://avatars3.githubusercontent.com/u/30119111?v=4" width="110px;"/><br /><sub>GodUseVPN</sub>](https://github.com/godusevpn)<br />[🌍](#translation-godusevpn "Translation") | [<img src="https://avatars1.githubusercontent.com/u/745576?v=4" width="110px;"/><br /><sub>周周</sub>](https://github.com/EngrZhou)<br />[🌍](#translation-EngrZhou "Translation") | [<img src="https://avatars3.githubusercontent.com/u/1631095?v=4" width="110px;"/><br /><sub>Sam</sub>](https://github.com/takuy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [<img src="https://avatars1.githubusercontent.com/u/264022?v=4" width="110px;"/><br /><sub>Azerothian</sub>](https://www.illisian.com.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [<img src="https://avatars1.githubusercontent.com/u/7632599?v=4" width="110px;"/><br /><sub>Tim Farmer</sub>](https://github.com/timothyfarmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -61,7 +61,7 @@ class LdapSync extends Command
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::error($e);
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class LdapSync extends Command
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::error($e);
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -198,8 +208,7 @@ class LdapSync extends Command
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$user->notes = 'Imported from LDAP';
|
||||
|
||||
$user->ldap_import = 1;
|
||||
|
||||
$errors = '';
|
||||
|
||||
@@ -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.'),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -57,12 +57,12 @@ class SendExpectedCheckinAlerts extends Command
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipient = new \App\Models\Recipients\AlertRecipient();
|
||||
|
||||
if (($assets) && ($assets->count() > 0) && ($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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,26 +50,28 @@ class SendExpirationAlerts extends Command
|
||||
$threshold = $settings->alert_interval;
|
||||
|
||||
|
||||
// Expiring Assets
|
||||
$assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count'=>$assets->count(), 'threshold' => $threshold]));
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == 1)) {
|
||||
|
||||
// Expiring licenses
|
||||
$licenses = License::getExpiringLicenses($threshold);
|
||||
|
||||
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count'=>$licenses->count(), 'threshold' => $threshold]));
|
||||
|
||||
$recipient = new \App\Models\Recipients\AlertRecipient();
|
||||
|
||||
if ((Setting::getSettings()->alert_email!='') && ($settings->alerts_enabled==1)) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
// Expiring Assets
|
||||
$assets = Asset::getExpiringWarrantee(Setting::getSettings()->alert_interval);
|
||||
if ($assets->count() > 0) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipient->notify(new ExpiringAssetsNotification($assets, $threshold));
|
||||
$this->info(trans_choice('mail.assets_warrantee_alert', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringAssetsNotification($assets, $threshold));
|
||||
}
|
||||
|
||||
// Expiring licenses
|
||||
$licenses = License::getExpiringLicenses($threshold);
|
||||
|
||||
|
||||
if ($licenses->count() > 0) {
|
||||
$recipient->notify(new ExpiringLicenseNotification($licenses, $threshold));
|
||||
$this->info(trans_choice('mail.license_expiring_alert', $licenses->count(), ['count' => $licenses->count(), 'threshold' => $threshold]));
|
||||
\Notification::send($recipients, new ExpiringLicenseNotification($licenses, $threshold));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,13 +50,14 @@ class SendInventoryAlerts extends Command
|
||||
$items = Helper::checkLowInventory();
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipient = new \App\Models\Recipients\AlertRecipient();
|
||||
|
||||
if (($items) && (count($items) > 0) && ($settings->alert_email!='')) {
|
||||
|
||||
$this->info( trans_choice('mail.low_inventory_alert',count($items)) );
|
||||
|
||||
$recipient->notify(new InventoryAlert($items, $settings->alert_threshold));
|
||||
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 {
|
||||
|
||||
91
app/Console/Commands/SendUpcomingAuditReport.php
Normal file
91
app/Console/Commands/SendUpcomingAuditReport.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Notifications\ExpiringAssetsNotification;
|
||||
use App\Models\Recipients;
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Notifications\SendUpcomingAuditNotification;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SendUpcomingAuditReport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:upcoming-audits';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send email/slack notifications for upcoming asset audits.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->alert_email != '') && ($settings->audit_warning_days) && ($settings->alerts_enabled == 1)) {
|
||||
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
|
||||
return new \App\Models\Recipients\AlertRecipient($item);
|
||||
});
|
||||
|
||||
|
||||
// Assets due for auditing
|
||||
|
||||
$assets = Asset::whereNotNull('next_audit_date')
|
||||
->DueOrOverdueForAudit($settings)
|
||||
->orderBy('last_audit_date', 'asc')->get();
|
||||
|
||||
if ($assets->count() > 0) {
|
||||
|
||||
$this->info(trans_choice('mail.upcoming-audits', $assets->count(),
|
||||
['count' => $assets->count(), 'threshold' => $settings->audit_warning_days]));
|
||||
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
|
||||
$this->info('Audit report sent to '.$settings->alert_email);
|
||||
} else {
|
||||
$this->info('No assets to be audited. No report sent.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
} elseif ($settings->alert_email=='') {
|
||||
$this->error('Could not send email. No alert email configured in settings');
|
||||
} elseif (!$settings->audit_warning_days) {
|
||||
$this->error('No audit warning days set in Admin Notifications. No mail will be sent.');
|
||||
} elseif ($settings->alerts_enabled!=1) {
|
||||
$this->info('Alerts are disabled in the settings. No mail will be sent');
|
||||
} else {
|
||||
$this->error('Something went wrong. :( ');
|
||||
$this->error('Admin Notifications Email Setting: '.$settings->alert_email);
|
||||
$this->error('Admin Audit Warning Setting: '.$settings->audit_warning_days);
|
||||
$this->error('Admin Alerts Emnabled: '.$settings->alerts_enabled);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\RestoreDeletedUsers;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
@@ -31,6 +32,7 @@ class Kernel extends ConsoleKernel
|
||||
Commands\RegenerateAssetTags::class,
|
||||
Commands\SyncAssetCounters::class,
|
||||
Commands\RestoreDeletedUsers::class,
|
||||
Commands\SendUpcomingAuditReport::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,7 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command('snipeit:expected-checkin')->daily();
|
||||
$schedule->command('snipeit:backup')->weekly();
|
||||
$schedule->command('backup:clean')->daily();
|
||||
$schedule->command('snipeit:upcoming-audits')->daily();
|
||||
}
|
||||
|
||||
protected function commands()
|
||||
|
||||
@@ -46,7 +46,7 @@ class AccessoriesController extends Controller
|
||||
$accessories->where('supplier_id','=',$request->input('supplier_id'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($accessories) && (request('offset') > $accessories->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -44,7 +44,7 @@ class AssetMaintenancesController extends Controller
|
||||
$maintenances->where('asset_id', '=', $request->input('asset_id'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($maintenances) && (request('offset') > $maintenances->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$allowed_columns = [
|
||||
|
||||
@@ -60,7 +60,7 @@ class AssetModelsController extends Controller
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($assetmodels) && (request('offset') > $assetmodels->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'models.created_at';
|
||||
@@ -179,7 +179,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$assetmodel->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class AssetsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index(Request $request, $audit = null)
|
||||
{
|
||||
|
||||
$this->authorize('index', Asset::class);
|
||||
@@ -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')) {
|
||||
@@ -144,10 +144,25 @@ class AssetsController extends Controller
|
||||
|
||||
$request->has('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($assets) && (request('offset') > $assets->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
// This is used by the audit reporting routes
|
||||
if (Gate::allows('audit', Asset::class)) {
|
||||
|
||||
switch ($audit) {
|
||||
case 'due':
|
||||
$assets->DueOrOverdueForAudit($settings);
|
||||
break;
|
||||
case 'overdue':
|
||||
$assets->overdueForAudit($settings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This is used by the sidenav, mostly
|
||||
|
||||
// We switched from using query scopes here because of a Laravel bug
|
||||
@@ -167,12 +182,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 +203,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 +222,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 +248,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 +306,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 +320,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 +369,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 +394,7 @@ class AssetsController extends Controller
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType()=='pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
}
|
||||
@@ -468,43 +484,15 @@ class AssetsController extends Controller
|
||||
$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 +507,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 +619,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 +627,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')));
|
||||
|
||||
@@ -30,7 +30,7 @@ class CategoriesController extends Controller
|
||||
$categories = $categories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($categories) && (request('offset') > $categories->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count';
|
||||
|
||||
@@ -41,7 +41,7 @@ class CompaniesController extends Controller
|
||||
$companies->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($companies) && (request('offset') > $companies->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -43,7 +43,7 @@ class ComponentsController extends Controller
|
||||
$components->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($components) && (request('offset') > $components->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$allowed_columns = ['id','name','min_amt','order_number','serial','purchase_date','purchase_cost','company','category','qty','location','image'];
|
||||
|
||||
@@ -35,12 +35,16 @@ class ConsumablesController extends Controller
|
||||
$consumables->where('company_id','=',$request->input('company_id'));
|
||||
}
|
||||
|
||||
if ($request->has('category_id')) {
|
||||
$consumables->where('category_id','=',$request->input('category_id'));
|
||||
}
|
||||
|
||||
if ($request->has('manufacturer_id')) {
|
||||
$consumables->where('manufacturer_id','=',$request->input('manufacturer_id'));
|
||||
}
|
||||
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($consumables) && (request('offset') > $consumables->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
$allowed_columns = ['id','name','order_number','min_amt','purchase_date','purchase_cost','company','category','model_number', 'item_no', 'manufacturer','location','qty','image'];
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -39,7 +39,7 @@ class DepartmentsController extends Controller
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($departments) && (request('offset') > $departments->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -28,7 +28,7 @@ class DepreciationsController extends Controller
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($depreciations) && (request('offset') > $depreciations->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -28,7 +28,7 @@ class GroupsController extends Controller
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($groups) && (request('offset') > $groups->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -25,7 +25,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
return (new ImportsTransformer)->transformImports($imports);
|
||||
|
||||
@@ -39,10 +39,8 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
//
|
||||
if (!Company::isCurrentUserAuthorized()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('general.insufficient_permissions'));
|
||||
} elseif (!config('app.lock_passwords')) {
|
||||
$this->authorize('import');
|
||||
if (!config('app.lock_passwords')) {
|
||||
$files = Input::file('files');
|
||||
$path = config('app.private_uploads').'/imports';
|
||||
$results = [];
|
||||
@@ -119,7 +117,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function process(ItemImportRequest $request, $import_id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
$errors = $request->import(Import::find($import_id));
|
||||
@@ -162,7 +160,7 @@ class ImportController extends Controller
|
||||
*/
|
||||
public function destroy($import_id)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
$import = Import::find($import_id);
|
||||
try {
|
||||
unlink(config('app.private_uploads').'/imports/'.$import->file_path);
|
||||
|
||||
@@ -82,7 +82,7 @@ class LicensesController extends Controller
|
||||
}
|
||||
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($licenses) && (request('offset') > $licenses->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
@@ -227,7 +227,8 @@ class LicensesController extends Controller
|
||||
|
||||
$seats = LicenseSeat::where('license_id', $licenseId)->with('license', 'user', 'asset');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($seats) && (request('offset') > $seats->count())) ? 0 : request('offset', 0);
|
||||
|
||||
$limit = request('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class LocationsController extends Controller
|
||||
|
||||
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($locations) && (request('offset') > $locations->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class ManufacturersController extends Controller
|
||||
|
||||
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($manufacturers) && (request('offset') > $manufacturers->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,20 @@ class ProfileController extends Controller
|
||||
|
||||
|
||||
foreach ($checkoutRequests as $checkoutRequest) {
|
||||
$results['rows'][] = [
|
||||
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
|
||||
'name' => $checkoutRequest->itemRequested()->present()->name(),
|
||||
'type' => $checkoutRequest->itemType(),
|
||||
'qty' => $checkoutRequest->quantity,
|
||||
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
|
||||
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
// Make sure the asset and request still exist
|
||||
if ($checkoutRequest && $checkoutRequest->itemRequested()) {
|
||||
$results['rows'][] = [
|
||||
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
|
||||
'name' => $checkoutRequest->itemRequested()->present()->name(),
|
||||
'type' => $checkoutRequest->itemType(),
|
||||
'qty' => $checkoutRequest->quantity,
|
||||
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
|
||||
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class StatuslabelsController extends Controller
|
||||
$statuslabels = $statuslabels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = $request->input('offset', 0);
|
||||
$offset = (($statuslabels) && (request('offset') > $statuslabels->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
@@ -238,8 +238,7 @@ class StatuslabelsController extends Controller
|
||||
*/
|
||||
public function checkIfDeployable($id) {
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
|
||||
$this->authorize('view', $statuslabel);
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
if ($statuslabel->getStatuslabelType()=='deployable') {
|
||||
return '1';
|
||||
|
||||
@@ -33,7 +33,7 @@ class SuppliersController extends Controller
|
||||
$suppliers = $suppliers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($suppliers) && (request('offset') > $suppliers->count())) ? 0 : request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Http\Requests\SaveUserRequest;
|
||||
use App\Models\Asset;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
|
||||
class UsersController extends Controller
|
||||
{
|
||||
@@ -51,6 +52,7 @@ class UsersController extends Controller
|
||||
'users.phone',
|
||||
'users.state',
|
||||
'users.two_factor_enrolled',
|
||||
'users.two_factor_optin',
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
@@ -85,7 +87,7 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$offset = request('offset', 0);
|
||||
$offset = (($users) && (request('offset') > $users->count())) ? 0 : request('offset', 0);
|
||||
$limit = request('limit', 20);
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
@@ -147,8 +149,7 @@ class UsersController extends Controller
|
||||
$users = Company::scopeCompanyables($users);
|
||||
|
||||
if ($request->has('search')) {
|
||||
$users = $users->where('first_name', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('last_name', 'LIKE', '%'.$request->get('search').'%')
|
||||
$users = $users->SimpleNameSearch($request->get('search'))
|
||||
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
@@ -200,6 +201,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()));
|
||||
@@ -296,6 +303,23 @@ class UsersController extends Controller
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of accessories assigned to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.6.14]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function accessories($id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$user = User::findOrFail($id);
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessories = $user->accessories;
|
||||
return (new AccessoriesTransformer)->transformAccessories($accessories, $accessories->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the user's two-factor status
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
unlink(app('models_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ class AssetModelsController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/models/'.$model->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
// Create the image (if one was chosen.)
|
||||
if ($request->hasFile('image')) {
|
||||
if ($request->has('image')) {
|
||||
$image = $request->input('image');
|
||||
|
||||
// After modification, the image is prefixed by mime info like the following:
|
||||
@@ -326,7 +326,7 @@ class AssetsController extends Controller
|
||||
unlink(public_path().'/uploads/assets/'.$asset->image);
|
||||
$asset->image = '';
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -744,6 +744,18 @@ class AssetsController extends Controller
|
||||
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
public function dueForAudit()
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
return view('hardware/audit-due');
|
||||
}
|
||||
|
||||
public function overdueForAudit()
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
return view('hardware/audit-overdue');
|
||||
}
|
||||
|
||||
|
||||
public function auditStore(AssetFileRequest $request, $id)
|
||||
{
|
||||
@@ -781,7 +793,7 @@ class AssetsController extends Controller
|
||||
$filename = 'audit-'.$asset->id.'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$extension;
|
||||
$file->move($destinationPath, $filename);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ 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) {
|
||||
@@ -77,45 +77,45 @@ class LoginController extends Controller
|
||||
|
||||
try {
|
||||
$user = User::where('username', '=', $remote_user)->whereNull('deleted_at')->where('activated', '=', '1')->first();
|
||||
LOG::debug("Remote user auth lookup complete");
|
||||
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)->where('activated', '=', '1')->first();
|
||||
LOG::debug("Local auth lookup complete");
|
||||
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);
|
||||
|
||||
@@ -161,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);
|
||||
@@ -169,13 +169,13 @@ 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(['username' => $request->input('username'), 'password' => $request->input('password'), 'activated' => 1], $request->input('remember'))) {
|
||||
|
||||
@@ -183,7 +183,7 @@ class LoginController extends Controller
|
||||
$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 {
|
||||
|
||||
@@ -209,26 +209,33 @@ class LoginController extends Controller
|
||||
public function getTwoFactorEnroll()
|
||||
{
|
||||
|
||||
// Make sure the user is logged in
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$user = Auth::user();
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
|
||||
if ($user->two_factor_secret=='') {
|
||||
$user->two_factor_secret = $google2fa->generateSecretKey(32);
|
||||
$user->save();
|
||||
// We wouldn't normally see this page if 2FA isn't enforced via the
|
||||
// \App\Http\Middleware\CheckForTwoFactor middleware AND if a device isn't enrolled,
|
||||
// but let's check check anyway in case there's a browser history or back button thing.
|
||||
// While you can access this page directly, enrolling a device when 2FA isn't enforced
|
||||
// won't cause any harm.
|
||||
|
||||
if (($user->two_factor_secret!='') && ($user->two_factor_enrolled==1)) {
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.already_enrolled'));
|
||||
}
|
||||
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $google2fa->generateSecretKey();
|
||||
$user->two_factor_secret = $secret;
|
||||
$user->save();
|
||||
|
||||
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
|
||||
urlencode(Setting::getSettings()->site_name),
|
||||
urlencode($user->username),
|
||||
$user->two_factor_secret
|
||||
);
|
||||
|
||||
return view('auth.two_factor_enroll')->with('google2fa_url', $google2fa_url);
|
||||
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
||||
$barcode_obj = $barcode->getBarcodeObj('QRCODE', 'otpauth://totp/'.urlencode($settings->site_name).':'.urlencode($user->username).'?secret='.urlencode($secret).'&issuer=Snipe-IT&period=30', 300, 300, 'black', array(-2, -2, -2, -2));
|
||||
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
|
||||
|
||||
}
|
||||
|
||||
@@ -240,6 +247,20 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function getTwoFactorAuth()
|
||||
{
|
||||
// Check that the user is logged in
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
// Check whether there is a device enrolled.
|
||||
// This *should* be handled via the \App\Http\Middleware\CheckForTwoFactor middleware
|
||||
// but we're just making sure (in case someone edited the database directly, etc)
|
||||
if (($user->two_factor_secret=='') || ($user->two_factor_enrolled!=1)) {
|
||||
return redirect()->route('two-factor-enroll');
|
||||
}
|
||||
|
||||
return view('auth.two_factor');
|
||||
}
|
||||
|
||||
@@ -252,22 +273,25 @@ class LoginController extends Controller
|
||||
{
|
||||
|
||||
if (!Auth::check()) {
|
||||
return redirect()->route('login')->with('error', 'You must be logged in.');
|
||||
return redirect()->route('login')->with('error', trans('auth/general.login_prompt'));
|
||||
}
|
||||
|
||||
if (!$request->has('two_factor_secret')) {
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.code_required'));
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$secret = $request->get('two_factor_secret');
|
||||
$google2fa = app()->make('PragmaRX\Google2FA\Contracts\Google2FA');
|
||||
$valid = $google2fa->verifyKey($user->two_factor_secret, $secret);
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $request->input('two_factor_secret');
|
||||
|
||||
if ($valid) {
|
||||
if ($google2fa->verifyKey($user->two_factor_secret, $secret)) {
|
||||
$user->two_factor_enrolled = 1;
|
||||
$user->save();
|
||||
$request->session()->put('2fa_authed', 'true');
|
||||
return redirect()->route('home')->with('success', 'You are logged in!');
|
||||
}
|
||||
|
||||
return redirect()->route('two-factor')->with('error', 'Invalid two-factor code');
|
||||
return redirect()->route('two-factor')->with('error', trans('auth/message.two_factor.invalid_code'));
|
||||
|
||||
|
||||
}
|
||||
@@ -290,7 +314,7 @@ class LoginController extends Controller
|
||||
return redirect()->away($customLogoutUrl);
|
||||
}
|
||||
|
||||
return redirect()->route('login')->with('success', 'You have successfully logged out!');
|
||||
return redirect()->route('login')->with('success', trans('auth/general.logout.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -315,11 +339,11 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the user after determining they are locked out.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
* Redirect the user after determining they are locked out.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendLockoutResponse(Request $request)
|
||||
{
|
||||
$seconds = $this->limiter()->availableIn(
|
||||
@@ -330,18 +354,18 @@ class LoginController extends Controller
|
||||
|
||||
$message = \Lang::get('auth/message.throttle', ['minutes' => $minutes]);
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->back()
|
||||
->withInput($request->only($this->username(), 'remember'))
|
||||
->withErrors([$this->username() => $message]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override the lockout time and duration
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
* Override the lockout time and duration
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasTooManyLoginAttempts(Request $request)
|
||||
{
|
||||
$lockoutTime = config('auth.throttle.lockout_duration');
|
||||
|
||||
@@ -179,7 +179,7 @@ class CategoriesController extends Controller
|
||||
try {
|
||||
unlink(app('categories_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ final class CompaniesController extends Controller
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
return view('companies/index')->with('companies', Company::all());
|
||||
return view('companies/index');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +148,7 @@ final class CompaniesController extends Controller
|
||||
try {
|
||||
unlink(app('companies_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class DepartmentsController extends Controller
|
||||
try {
|
||||
unlink(app('departments_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class DepreciationsController extends Controller
|
||||
$this->authorize('view', Depreciation::class);
|
||||
|
||||
// Show the page
|
||||
return view('depreciations/index', compact('depreciations'));
|
||||
return view('depreciations/index');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,7 +12,7 @@ class ImportsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$this->authorize('import');
|
||||
$imports = Import::latest()->get();
|
||||
$imports = (new ImportsTransformer)->transformImports($imports);
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
|
||||
@@ -413,20 +413,7 @@ class LicensesController extends Controller
|
||||
return redirect()->back()->withInput();
|
||||
}
|
||||
|
||||
// Declare the rules for the form validation
|
||||
$rules = array(
|
||||
'note' => 'string',
|
||||
'notes' => 'string',
|
||||
);
|
||||
|
||||
// Create a new validator instance from our validation rules
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
|
||||
// If validation fails, we'll exit the operation now.
|
||||
if ($validator->fails()) {
|
||||
// Ooops.. something went wrong
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
if (!$return_to) {
|
||||
$return_to = Asset::find($licenseSeat->asset_id);
|
||||
@@ -438,7 +425,7 @@ class LicensesController extends Controller
|
||||
|
||||
// Was the asset updated?
|
||||
if ($licenseSeat->save()) {
|
||||
$licenseSeat->logCheckin($return_to, e(request('note')));
|
||||
$licenseSeat->logCheckin($license, e(request('note')));
|
||||
if ($backTo=='user') {
|
||||
return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success'));
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ class LocationsController extends Controller
|
||||
try {
|
||||
unlink(app('locations_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ class ManufacturersController extends Controller
|
||||
try {
|
||||
unlink(app('manufacturers_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ class ManufacturersController extends Controller
|
||||
try {
|
||||
unlink(public_path().'/uploads/manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ class ProfileController extends Controller
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->website = $request->input('website');
|
||||
$user->gravatar = $request->input('gravatar');
|
||||
$user->phone = $request->input('phone');
|
||||
|
||||
|
||||
|
||||
if (!config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en');
|
||||
@@ -125,8 +128,7 @@ class ProfileController extends Controller
|
||||
|
||||
$rules = array(
|
||||
'current_password' => 'required',
|
||||
'password' => Setting::passwordComplexityRulesSaving('store'),
|
||||
'password_confirm' => 'required|same:password',
|
||||
'password' => Setting::passwordComplexityRulesSaving('store').'|confirmed',
|
||||
);
|
||||
|
||||
$validator = \Validator::make($request->all(), $rules);
|
||||
|
||||
@@ -603,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')) {
|
||||
|
||||
@@ -482,13 +482,12 @@ class SettingsController extends Controller
|
||||
$setting->two_factor_enabled = null;
|
||||
} else {
|
||||
$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');
|
||||
}
|
||||
|
||||
# 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');
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +174,7 @@ class SuppliersController extends Controller
|
||||
try {
|
||||
unlink(app('suppliers_upload_path').$old_image);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
\Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -259,9 +259,7 @@ class UsersController extends Controller
|
||||
|
||||
// Only save groups if the user is a super user
|
||||
if (Auth::user()->isSuperUser()) {
|
||||
if ($request->has('groups')) {
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
$user->groups()->sync($request->input('groups'));
|
||||
}
|
||||
|
||||
|
||||
@@ -984,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'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class Kernel extends HttpKernel
|
||||
\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,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,20 @@ class ItemImportRequest extends FormRequest
|
||||
$import->save();
|
||||
$fieldMappings=[];
|
||||
if ($import->field_map) {
|
||||
|
||||
// This checks to make sure the field header has been mapped.
|
||||
// If it hasn't been, it will throw an array_flip error
|
||||
foreach ($import->field_map as $field => $fieldValue) {
|
||||
$errorMessage = null;
|
||||
|
||||
if(is_null($fieldValue)){
|
||||
$errorMessage = 'All import fields must be mapped.';
|
||||
$this->errorCallback($import, $field, $errorMessage);
|
||||
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
|
||||
// We submit as csv field: column, but the importer is happier if we flip it here.
|
||||
$fieldMappings = array_change_key_case(array_flip($import->field_map), CASE_LOWER);
|
||||
// dd($fieldMappings);
|
||||
@@ -50,6 +64,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 +75,7 @@ class ItemImportRequest extends FormRequest
|
||||
|
||||
public function log($string)
|
||||
{
|
||||
// \Log::Info($string);
|
||||
\Log::Info($string);
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
|
||||
@@ -37,7 +37,7 @@ class SaveUserRequest extends Request
|
||||
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
|
||||
if ($this->request->get('ldap_import') == false)
|
||||
{
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('store');
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('store').'|confirmed';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class SaveUserRequest extends Request
|
||||
case 'PUT':
|
||||
$rules['first_name'] = 'required|string|min:1';
|
||||
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update');
|
||||
$rules['password'] = Setting::passwordComplexityRulesSaving('update').'|confirmed';
|
||||
break;
|
||||
|
||||
// Save only what's passed
|
||||
@@ -58,9 +58,7 @@ class SaveUserRequest extends Request
|
||||
|
||||
default:break;
|
||||
}
|
||||
|
||||
$rules['password_confirm'] = 'sometimes|required_with:password';
|
||||
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ class SetupUserRequest extends Request
|
||||
'last_name' => 'required|string|min:1',
|
||||
'username' => 'required|string|min:2|unique:users,username,NULL,deleted_at',
|
||||
'email' => 'email|unique:users,email',
|
||||
'password' => 'required|min:6',
|
||||
'password_confirm' => 'required|min:6|same:password',
|
||||
'password' => 'required|min:6|confirmed',
|
||||
'email_domain' => 'required|min:4',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -26,6 +26,18 @@ class ActionlogsTransformer
|
||||
if ($actionlog->filename!='') {
|
||||
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
|
||||
}
|
||||
|
||||
// This is necessary since we can't escape special characters within a JSON object
|
||||
if (($actionlog->log_meta) && ($actionlog->log_meta!='')) {
|
||||
$meta_array = json_decode($actionlog->log_meta);
|
||||
foreach ($meta_array as $key => $value) {
|
||||
foreach ($value as $meta_key => $meta_value) {
|
||||
$clean_meta[$key][$meta_key] = e($meta_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$array = [
|
||||
'id' => (int) $actionlog->id,
|
||||
'icon' => $icon,
|
||||
@@ -64,7 +76,7 @@ class ActionlogsTransformer
|
||||
|
||||
'note' => ($actionlog->note) ? e($actionlog->note): null,
|
||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||
'log_meta' => ($actionlog->log_meta) ? json_decode($actionlog->log_meta): null,
|
||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||
|
||||
|
||||
];
|
||||
|
||||
@@ -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)) ? [
|
||||
|
||||
@@ -24,7 +24,7 @@ class UsersTransformer
|
||||
$array = [
|
||||
'id' => (int) $user->id,
|
||||
'avatar' => e($user->present()->gravatar),
|
||||
'name' => e($user->first_name).' '.($user->last_name),
|
||||
'name' => e($user->first_name).' '.e($user->last_name),
|
||||
'first_name' => e($user->first_name),
|
||||
'last_name' => e($user->last_name),
|
||||
'username' => e($user->username),
|
||||
@@ -53,6 +53,8 @@ class UsersTransformer
|
||||
'permissions' => $user->decodePermissions(),
|
||||
'activated' => ($user->activated =='1') ? true : false,
|
||||
'two_factor_activated' => ($user->two_factor_active()) ? true : false,
|
||||
'two_factor_enrolled' => ($user->two_factor_active_and_enrolled()) ? true : false,
|
||||
|
||||
'assets_count' => (int) $user->assets_count,
|
||||
'licenses_count' => (int) $user->licenses_count,
|
||||
'accessories_count' => (int) $user->accessories_count,
|
||||
|
||||
@@ -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,6 +30,7 @@ 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',
|
||||
@@ -66,6 +68,9 @@ abstract class Importer
|
||||
'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
|
||||
@@ -176,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 ]));
|
||||
}
|
||||
@@ -198,7 +202,6 @@ abstract class Importer
|
||||
public function lookupCustomKey($key)
|
||||
{
|
||||
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.
|
||||
@@ -248,7 +251,10 @@ 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
|
||||
@@ -261,8 +267,13 @@ abstract class Importer
|
||||
$user_array = [
|
||||
'full_name' => $this->findCsvMatch($row, "full_name"),
|
||||
'email' => $this->findCsvMatch($row, "email"),
|
||||
'username' => $this->findCsvMatch($row, "username")
|
||||
'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');
|
||||
@@ -283,6 +294,7 @@ abstract class Importer
|
||||
$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') {
|
||||
@@ -290,8 +302,9 @@ abstract class Importer
|
||||
}
|
||||
}
|
||||
|
||||
// Does this ever actually fire??
|
||||
// Check for a matching user after trying to guess username.
|
||||
if($user = User::where('username', $user_array['username'])->first()) {
|
||||
if ($user = User::where('username', $user_array['username'])->first()) {
|
||||
$this->log('User '.$user_array['username'].' already exists');
|
||||
return $user;
|
||||
}
|
||||
@@ -307,10 +320,15 @@ abstract class Importer
|
||||
$user->last_name = $user_array['last_name'];
|
||||
$user->username = $user_array['username'];
|
||||
$user->email = $user_array['email'];
|
||||
$user->activated = 1;
|
||||
$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;
|
||||
}
|
||||
@@ -360,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
|
||||
*
|
||||
@@ -407,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");
|
||||
@@ -84,7 +97,7 @@ class ItemImporter extends Importer
|
||||
*/
|
||||
protected function determineCheckout($row)
|
||||
{
|
||||
// We only support checkout-to-location for asset, so short circuit otherw.
|
||||
// We only support checkout-to-location for asset, so short circuit otherwise.
|
||||
if(get_class($this) != AssetImporter::class) {
|
||||
return $this->createOrFetchUser($row);
|
||||
}
|
||||
@@ -161,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
|
||||
*/
|
||||
@@ -279,6 +292,8 @@ class ItemImporter extends Importer
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the existing status label or create new if it doesn't exist.
|
||||
*
|
||||
|
||||
@@ -6,13 +6,21 @@ 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)
|
||||
@@ -31,25 +39,40 @@ class UserImporter extends ItemImporter
|
||||
*/
|
||||
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['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->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;
|
||||
@@ -57,10 +80,11 @@ class UserImporter extends ItemImporter
|
||||
$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) {
|
||||
|
||||
if(($user->email) && ($user->activated=='1')) {
|
||||
$data = [
|
||||
'email' => $user->email,
|
||||
'username' => $user->username,
|
||||
@@ -69,8 +93,10 @@ class UserImporter extends ItemImporter
|
||||
'password' => $this->tempPassword,
|
||||
];
|
||||
|
||||
// UNCOMMENT this to re-enable sending email notifications on user import
|
||||
// $user->notify(new WelcomeNotification($data));
|
||||
if ($this->send_welcome) {
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
}
|
||||
$user = null;
|
||||
$this->item = null;
|
||||
|
||||
@@ -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 | | |
|
||||
|
||||
|
||||
@@ -796,6 +796,83 @@ class Asset extends Depreciable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for Assets that are due for auditing, based on the assets.next_audit_date
|
||||
* and settings.audit_warning_days.
|
||||
*
|
||||
* This is/will be used in the artisan command snipeit:upcoming-audits and also
|
||||
* for an upcoming API call for retrieving a report on assets that will need to be audited.
|
||||
*
|
||||
* Due for audit soon:
|
||||
* next_audit_date greater than or equal to now (must be in the future)
|
||||
* and (next_audit_date - threshold days) <= now ()
|
||||
*
|
||||
* Example:
|
||||
* next_audit_date = May 4, 2025
|
||||
* threshold for alerts = 30 days
|
||||
* now = May 4, 2019
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since v4.6.16
|
||||
* @param Setting $settings
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
|
||||
public function scopeDueForAudit($query, $settings)
|
||||
{
|
||||
return $query->whereNotNull('assets.next_audit_date')
|
||||
->where('assets.next_audit_date', '>=', Carbon::now())
|
||||
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $settings->audit_warning_days DAY) <= '".Carbon::now()."'")
|
||||
->where('assets.archived', '=', 0)
|
||||
->NotArchived();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for Assets that are OVERDUE for auditing, based on the assets.next_audit_date
|
||||
* and settings.audit_warning_days. It checks to see if assets.next audit_date is before now
|
||||
*
|
||||
* This is/will be used in the artisan command snipeit:upcoming-audits and also
|
||||
* for an upcoming API call for retrieving a report on overdue assets.
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since v4.6.16
|
||||
* @param Setting $settings
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
|
||||
public function scopeOverdueForAudit($query)
|
||||
{
|
||||
return $query->whereNotNull('assets.next_audit_date')
|
||||
->where('assets.next_audit_date', '<', Carbon::now())
|
||||
->where('assets.archived', '=', 0)
|
||||
->NotArchived();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for Assets that are due for auditing OR overdue, based on the assets.next_audit_date
|
||||
* and settings.audit_warning_days.
|
||||
*
|
||||
* This is/will be used in the artisan command snipeit:upcoming-audits and also
|
||||
* for an upcoming API call for retrieving a report on assets that will need to be audited.
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since v4.6.16
|
||||
* @param Setting $settings
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
|
||||
public function scopeDueOrOverdueForAudit($query, $settings)
|
||||
{
|
||||
return $query->whereNotNull('assets.next_audit_date')
|
||||
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $settings->audit_warning_days DAY) <= '".Carbon::now()."'")
|
||||
->where('assets.archived', '=', 0)
|
||||
->NotArchived();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope for Archived assets
|
||||
*
|
||||
@@ -1180,8 +1257,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,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',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -35,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',
|
||||
);
|
||||
|
||||
@@ -76,7 +76,7 @@ class Consumable extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date'];
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
||||
@@ -25,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',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -96,8 +96,11 @@ class Ldap extends Model
|
||||
|
||||
$filterQuery = $settings->ldap_auth_filter_query . $username;
|
||||
|
||||
|
||||
if (!$ldapbind = @ldap_bind($connection, $userDn, $password)) {
|
||||
return false;
|
||||
if(!$ldapbind = Ldap::bindAdminToLdap($connection)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$results = ldap_search($connection, $baseDn, $filterQuery)) {
|
||||
@@ -112,7 +115,7 @@ class Ldap extends Model
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user;
|
||||
return array_change_key_case($user);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ class License extends Depreciable
|
||||
protected $searchableRelations = [
|
||||
'manufacturer' => ['name'],
|
||||
'company' => ['name'],
|
||||
'category' => ['name'],
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
|
||||
@@ -26,7 +26,7 @@ class Location extends SnipeModel
|
||||
'address' => 'max:80|nullable',
|
||||
'address2' => 'max:80|nullable',
|
||||
'zip' => 'min:3|max:10|nullable',
|
||||
// 'manager_id' => 'exists:users'
|
||||
'manager_id' => 'exists:users,id|nullable'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,7 @@ class Location extends SnipeModel
|
||||
'zip',
|
||||
'ldap_ou',
|
||||
'currency',
|
||||
'manager_id',
|
||||
'image',
|
||||
];
|
||||
protected $hidden = ['user_id'];
|
||||
|
||||
@@ -41,7 +41,8 @@ trait Loggable
|
||||
$settings = Setting::getSettings();
|
||||
$log = new Actionlog;
|
||||
$log = $this->determineLogItemType($log);
|
||||
$log->user_id = Auth::user()->id;
|
||||
if(Auth::user())
|
||||
$log->user_id = Auth::user()->id;
|
||||
|
||||
if (!isset($target)) {
|
||||
throw new Exception('All checkout logs require a target');
|
||||
|
||||
@@ -5,10 +5,9 @@ use App\Models\Setting;
|
||||
|
||||
class AlertRecipient extends Recipient{
|
||||
|
||||
public function __construct()
|
||||
public function __construct(string $email)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$this->email = $settings->alert_email;
|
||||
$this->email = trim($email);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
'manager_id',
|
||||
'password',
|
||||
'phone',
|
||||
'notes',
|
||||
'state',
|
||||
'username',
|
||||
'zip',
|
||||
@@ -166,11 +167,6 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
return $this->belongsTo('\App\Models\Department', 'department_id');
|
||||
}
|
||||
|
||||
public function isActivated()
|
||||
{
|
||||
return $this->activated ==1;
|
||||
}
|
||||
|
||||
public function getFullNameAttribute()
|
||||
{
|
||||
return $this->first_name . " " . $this->last_name;
|
||||
@@ -274,7 +270,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
**/
|
||||
public function managedLocations()
|
||||
{
|
||||
return $this->hasMany('\App\Models\Location', 'manager_id')->withTrashed();
|
||||
return $this->hasMany('\App\Models\Location', 'manager_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,7 +329,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
|
||||
public function scopeGetDeleted($query)
|
||||
{
|
||||
return $query->withTrashed()->whereNotNull('deleted_at');
|
||||
return $query->withTrashed()->whereNotNull('users.deleted_at');
|
||||
}
|
||||
|
||||
public function scopeGetNotDeleted($query)
|
||||
@@ -393,7 +389,11 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether two-factor authorization is required and the user has activated it
|
||||
* Check whether two-factor authorization is requiredfor this user
|
||||
*
|
||||
* 0 = 2FA disabled
|
||||
* 1 = 2FA optional
|
||||
* 2 = 2FA universally required
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
@@ -402,10 +402,45 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
*/
|
||||
public function two_factor_active () {
|
||||
|
||||
if (Setting::getSettings()->two_factor_enabled !='0') {
|
||||
if (($this->two_factor_optin =='1') && ($this->two_factor_enrolled)) {
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is optional and the user has opted in
|
||||
if ((Setting::getSettings()->two_factor_enabled =='1') && ($this->two_factor_optin =='1'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is required for everyone so is implicitly active
|
||||
elseif (Setting::getSettings()->two_factor_enabled =='2')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether two-factor authorization is required and the user has activated it
|
||||
* and enrolled a device
|
||||
*
|
||||
* 0 = 2FA disabled
|
||||
* 1 = 2FA optional
|
||||
* 2 = 2FA universally required
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.6.14]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function two_factor_active_and_enrolled () {
|
||||
|
||||
// If the 2FA is optional and the user has opted in and is enrolled
|
||||
if ((Setting::getSettings()->two_factor_enabled =='1') && ($this->two_factor_optin =='1') && ($this->two_factor_enrolled =='1'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// If the 2FA is required for everyone and the user has enrolled
|
||||
elseif ((Setting::getSettings()->two_factor_enabled =='2') && ($this->two_factor_enrolled))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -417,6 +452,25 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
return json_decode($this->permissions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search user by name with spaces in it.
|
||||
* We don't use the advancedTextSearch() scope because that searches
|
||||
* all of the relations as well, which is more than what we need.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param array $terms The search terms
|
||||
* @return \Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function scopeSimpleNameSearch($query, $search) {
|
||||
|
||||
$query = $query->where('first_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('last_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%$search%", "%$search%"]);
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
*
|
||||
@@ -450,7 +504,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
||||
|
||||
public function scopeDeleted($query)
|
||||
{
|
||||
return $query->whereNotNull('deleted_at');
|
||||
return $query->whereNotNull('users.deleted_at');
|
||||
}
|
||||
|
||||
|
||||
|
||||
67
app/Notifications/SendUpcomingAuditNotification.php
Normal file
67
app/Notifications/SendUpcomingAuditNotification.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class SendUpcomingAuditNotification extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($params, $threshold)
|
||||
{
|
||||
$this->assets = $params;
|
||||
$this->threshold = $threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return $notifyBy = ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.upcoming-audits',
|
||||
[
|
||||
'assets' => $this->assets,
|
||||
'threshold' => $this->threshold,
|
||||
])
|
||||
->subject(trans_choice('mail.upcoming-audits', $this->assets->count(), ['count' => $this->assets->count(), 'threshold' => $this->threshold]));
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ abstract class SnipePermissionsPolicy
|
||||
/**
|
||||
* Determine whether the user can view the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, $item = null)
|
||||
@@ -64,7 +64,7 @@ abstract class SnipePermissionsPolicy
|
||||
/**
|
||||
* Determine whether the user can create accessories.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
@@ -75,7 +75,7 @@ abstract class SnipePermissionsPolicy
|
||||
/**
|
||||
* Determine whether the user can update the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, $item = null)
|
||||
@@ -86,7 +86,7 @@ abstract class SnipePermissionsPolicy
|
||||
/**
|
||||
* Determine whether the user can delete the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, $item = null)
|
||||
@@ -97,11 +97,13 @@ abstract class SnipePermissionsPolicy
|
||||
/**
|
||||
* Determine whether the user can manage the accessory.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function manage(User $user, $item = null)
|
||||
{
|
||||
return $user->hasAccess($this->columnName().'.edit');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
273
app/Presenters/AssetAuditPresenter.php
Normal file
273
app/Presenters/AssetAuditPresenter.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
namespace App\Presenters;
|
||||
|
||||
use App\Models\CustomField;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Class AssetPresenter
|
||||
* @package App\Presenters
|
||||
*/
|
||||
class AssetAuditPresenter extends Presenter
|
||||
{
|
||||
|
||||
/**
|
||||
* Json Column Layout for bootstrap table
|
||||
* @return string
|
||||
*/
|
||||
public static function dataTableLayout()
|
||||
{
|
||||
$layout = [
|
||||
[
|
||||
"field" => "id",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.id'),
|
||||
"visible" => false
|
||||
], [
|
||||
"field" => "company",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.company'),
|
||||
"visible" => false,
|
||||
"formatter" => 'assetCompanyObjFilterFormatter'
|
||||
], [
|
||||
"field" => "name",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/form.name'),
|
||||
"visible" => true,
|
||||
"formatter" => "hardwareLinkFormatter"
|
||||
], [
|
||||
"field" => "image",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/hardware/table.image'),
|
||||
"visible" => false,
|
||||
"formatter" => "imageFormatter"
|
||||
], [
|
||||
"field" => "asset_tag",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/table.asset_tag'),
|
||||
"visible" => true,
|
||||
"formatter" => "hardwareLinkFormatter"
|
||||
], [
|
||||
"field" => "serial",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/form.serial'),
|
||||
"visible" => true,
|
||||
"formatter" => "hardwareLinkFormatter"
|
||||
], [
|
||||
"field" => "model",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/form.model'),
|
||||
"visible" => true,
|
||||
"formatter" => "modelsLinkObjFormatter"
|
||||
], [
|
||||
"field" => "model_number",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/models/table.modelnumber'),
|
||||
"visible" => false
|
||||
], [
|
||||
"field" => "category",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('general.category'),
|
||||
"visible" => false,
|
||||
"formatter" => "categoriesLinkObjFormatter"
|
||||
], [
|
||||
"field" => "status_label",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/table.status'),
|
||||
"visible" => true,
|
||||
"formatter" => "statuslabelsLinkObjFormatter"
|
||||
], [
|
||||
"field" => "assigned_to",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/form.checkedout_to'),
|
||||
"visible" => true,
|
||||
"formatter" => "polymorphicItemFormatter"
|
||||
], [
|
||||
"field" => "location",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/table.location'),
|
||||
"visible" => true,
|
||||
"formatter" => "deployedLocationFormatter"
|
||||
], [
|
||||
"field" => "rtd_location",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/hardware/form.default_location'),
|
||||
"visible" => false,
|
||||
"formatter" => "deployedLocationFormatter"
|
||||
], [
|
||||
"field" => "manufacturer",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('general.manufacturer'),
|
||||
"visible" => false,
|
||||
"formatter" => "manufacturersLinkObjFormatter"
|
||||
], [
|
||||
"field" => "purchase_date",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.purchase_date'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "purchase_cost",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.purchase_cost'),
|
||||
"footerFormatter" => 'sumFormatter',
|
||||
], [
|
||||
"field" => "order_number",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.order_number'),
|
||||
'formatter' => "orderNumberObjFilterFormatter"
|
||||
], [
|
||||
"field" => "eol",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"visible" => false,
|
||||
"title" => trans('general.eol'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "warranty_months",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('admin/hardware/form.warranty')
|
||||
],[
|
||||
"field" => "warranty_expires",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"visible" => false,
|
||||
"title" => trans('admin/hardware/form.warranty_expires'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
],[
|
||||
"field" => "notes",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.notes'),
|
||||
|
||||
], [
|
||||
"field" => "checkout_counter",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.checkouts_count')
|
||||
|
||||
],[
|
||||
"field" => "checkin_counter",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.checkins_count')
|
||||
|
||||
], [
|
||||
"field" => "requests_counter",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.user_requests_count')
|
||||
|
||||
], [
|
||||
"field" => "created_at",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.created_at'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "updated_at",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('general.updated_at'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "last_checkout",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('admin/hardware/table.checkout_date'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "expected_checkin",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('admin/hardware/form.expected_checkin'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "last_audit_date",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => true,
|
||||
"title" => trans('general.last_audit'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
], [
|
||||
"field" => "next_audit_date",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"visible" => true,
|
||||
"title" => trans('general.next_audit_date'),
|
||||
"formatter" => "dateDisplayFormatter"
|
||||
],
|
||||
];
|
||||
|
||||
// This looks complicated, but we have to confirm that the custom fields exist in custom fieldsets
|
||||
// *and* those fieldsets are associated with models, otherwise we'll trigger
|
||||
// javascript errors on the bootstrap tables side of things, since we're asking for properties
|
||||
// on fields that will never be passed through the REST API since they're not associated with
|
||||
// models. We only pass the fieldsets that pertain to each asset (via their model) so that we
|
||||
// don't junk up the REST API with tons of custom fields that don't apply
|
||||
|
||||
$fields = CustomField::whereHas('fieldset', function ($query) {
|
||||
$query->whereHas('models');
|
||||
})->get();
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$layout[] = [
|
||||
"field" => 'custom_fields.'.$field->convertUnicodeDbSlug(),
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"switchable" => true,
|
||||
"title" => ($field->field_encrypted=='1') ?'<i class="fa fa-lock"></i> '.e($field->name) : e($field->name),
|
||||
"formatter" => "customFieldsFormatter"
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
$layout[] = [
|
||||
"field" => "actions",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"switchable" => false,
|
||||
"title" => trans('table.actions'),
|
||||
"formatter" => "hardwareAuditFormatter",
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -391,7 +391,7 @@ class AssetPresenter extends Presenter
|
||||
public function eol_date()
|
||||
{
|
||||
|
||||
if (( $this->purchase_date ) && ( $this->model )) {
|
||||
if (( $this->purchase_date ) && ( $this->model ) && ($this->model->model->eol) ) {
|
||||
$date = date_create($this->purchase_date);
|
||||
date_add($date, date_interval_create_from_date_string($this->model->model->eol . ' months'));
|
||||
return date_format($date, 'Y-m-d');
|
||||
|
||||
@@ -226,14 +226,14 @@ class UserPresenter extends Presenter
|
||||
[
|
||||
"field" => "two_factor_enrolled",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/users/general.two_factor_enrolled'),
|
||||
"visible" => false,
|
||||
'formatter' => 'trueFalseFormatter'
|
||||
],
|
||||
[
|
||||
"field" => "two_factor_active",
|
||||
"field" => "two_factor_activated",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"switchable" => true,
|
||||
@@ -246,7 +246,7 @@ class UserPresenter extends Presenter
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.activated'),
|
||||
"title" => trans('general.login_enabled'),
|
||||
"visible" => true,
|
||||
'formatter' => 'trueFalseFormatter'
|
||||
],
|
||||
|
||||
@@ -58,7 +58,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
if (($this->app->environment('production')) && (config('services.rollbar.access_token'))){
|
||||
$this->app->register(\Rollbar\Laravel\RollbarServiceProvider::class);
|
||||
}
|
||||
|
||||
|
||||
foreach ($monolog->getHandlers() as $handler) {
|
||||
$handler->setLevel($log_level);
|
||||
}
|
||||
|
||||
@@ -113,6 +113,14 @@ class AuthServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
|
||||
// Can the user import CSVs?
|
||||
Gate::define('import', function ($user) {
|
||||
if ($user->hasAccess('import') ) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
# -----------------------------------------
|
||||
# Reports
|
||||
# -----------------------------------------
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
"doctrine/dbal": "^2.5.13",
|
||||
"doctrine/inflector": "1.1.*",
|
||||
"doctrine/instantiator": "1.0.*",
|
||||
"eduardokum/laravel-mail-auto-embed": "^1.0",
|
||||
"erusev/parsedown": "^1.6",
|
||||
"fideloper/proxy": "^3.1",
|
||||
"fideloper/proxy": "^3.3",
|
||||
"intervention/image": "^2.3",
|
||||
"javiereguiluz/easyslugger": "^1.0",
|
||||
"laravel/framework": "5.4.35",
|
||||
@@ -26,9 +27,10 @@
|
||||
"patchwork/utf8": "~1.2",
|
||||
"phpdocumentor/reflection-docblock": "3.2.2",
|
||||
"phpspec/prophecy": "1.6.2",
|
||||
"pragmarx/google2fa": "^1.0",
|
||||
"pragmarx/google2fa": "^5.0",
|
||||
"pragmarx/google2fa-laravel": "^0.3.0",
|
||||
"predis/predis": "^1.1",
|
||||
"rollbar/rollbar-laravel": "^2.2",
|
||||
"rollbar/rollbar-laravel": "2.4.1",
|
||||
"schuppo/password-strength": "~1.5",
|
||||
"spatie/laravel-backup": "3.11.0",
|
||||
"tecnickcom/tc-lib-barcode": "^1.15",
|
||||
@@ -41,7 +43,6 @@
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"phpunit/php-token-stream": "1.4.11",
|
||||
"phpunit/phpunit": "~5.7",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"squizlabs/php_codesniffer": "*",
|
||||
"symfony/css-selector": "3.1.*",
|
||||
"symfony/dom-crawler": "3.1.*"
|
||||
|
||||
886
composer.lock
generated
886
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -291,12 +291,13 @@ return [
|
||||
Collective\Html\HtmlServiceProvider::class,
|
||||
Spatie\Backup\BackupServiceProvider::class,
|
||||
Fideloper\Proxy\TrustedProxyServiceProvider::class,
|
||||
PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider::class,
|
||||
PragmaRX\Google2FALaravel\ServiceProvider::class,
|
||||
Laravel\Passport\PassportServiceProvider::class,
|
||||
Laravel\Tinker\TinkerServiceProvider::class,
|
||||
Unicodeveloper\DumbPassword\DumbPasswordServiceProvider::class,
|
||||
Schuppo\PasswordStrength\PasswordStrengthServiceProvider::class,
|
||||
Tightenco\Ziggy\ZiggyServiceProvider::class, // Laravel routes in vue
|
||||
Eduardokum\LaravelMailAutoEmbed\ServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
|
||||
@@ -86,6 +86,6 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => 'snipeit',
|
||||
'prefix' => env('CACHE_PREFIX', 'snipeit'),
|
||||
|
||||
];
|
||||
|
||||
@@ -27,6 +27,15 @@ return array(
|
||||
)
|
||||
),
|
||||
|
||||
'CSV Import' => array(
|
||||
array(
|
||||
'permission' => 'import',
|
||||
'label' => '',
|
||||
'note' => 'This will allow users to import even if access to users, assets, etc is denied elsewhere.',
|
||||
'display' => true,
|
||||
)
|
||||
),
|
||||
|
||||
'Reports' => array(
|
||||
array(
|
||||
'permission' => 'reports.view',
|
||||
|
||||
@@ -63,11 +63,12 @@ return [
|
||||
* We may also want to add something like:
|
||||
* \Illuminate\Http\Request::HEADER_CLIENT_PROTO => 'X_FORWARDED_SCHEME',
|
||||
*/
|
||||
// These are defaults already set in the config:
|
||||
'headers' => [
|
||||
(defined('Illuminate\Http\Request::HEADER_FORWARDED') ? Illuminate\Http\Request::HEADER_FORWARDED : 'forwarded') => 'FORWARDED',
|
||||
\Illuminate\Http\Request::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
|
||||
\Illuminate\Http\Request::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
|
||||
\Illuminate\Http\Request::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
|
||||
\Illuminate\Http\Request::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
|
||||
|
||||
]
|
||||
];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v4.6.4',
|
||||
'full_app_version' => 'v4.6.4 - build 3881-g522dc1db2',
|
||||
'build_version' => '3881',
|
||||
'app_version' => 'v4.6.16',
|
||||
'full_app_version' => 'v4.6.16 - build 4018-gce16eae50',
|
||||
'build_version' => '4018',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g522dc1db2',
|
||||
'full_hash' => 'v4.6.4-3881-g522dc1db2',
|
||||
'branch' => 'master',
|
||||
'hash_version' => 'gce16eae50',
|
||||
'full_hash' => 'v4.6.16-6-gce16eae50',
|
||||
'branch' => 'features/6204_email_audit-alerts',
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ class AddPrefixToSettings extends Migration {
|
||||
//
|
||||
Schema::table('settings', function(Blueprint $table) {
|
||||
|
||||
$table->string('auto_increment_prefix')->default(0);
|
||||
$table->string('auto_increment_prefix')->nullable()->default(NULL);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ChangeAutoIncrementPrefixToNullable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->string('auto_increment_prefix')->nullable()->default(null)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AutoIncrementBackToString extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->string('auto_increment_prefix')->nullable()->default(null)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MakeSerialNullable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$platform = Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
|
||||
$platform->registerDoctrineTypeMapping('enum', 'string');
|
||||
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->string('serial')->nullable()->default(null)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MakeFieldsNullableForIntegrity extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
|
||||
Schema::table('locations', function (Blueprint $table) {
|
||||
$table->string('city')->nullable()->default(null)->change();
|
||||
$table->string('state')->nullable()->default(null)->change();
|
||||
$table->string('country')->nullable()->default(null)->change();
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
$table->string('address')->nullable()->default(null)->change();
|
||||
$table->string('address2')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('last_name')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('suppliers', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('status_labels', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('models', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
$table->integer('manufacturer_id')->nullable()->default(null)->change();
|
||||
$table->integer('category_id')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('licenses', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
$table->boolean('maintained')->nullable()->default(null)->change();
|
||||
});
|
||||
|
||||
Schema::table('depreciations', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->default(null)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -43,5 +43,8 @@ class DatabaseSeeder extends Seeder
|
||||
\Log::info($output);
|
||||
|
||||
Model::reguard();
|
||||
|
||||
DB::table('imports')->truncate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ if [ -z "$APP_KEY" ]
|
||||
then
|
||||
echo "Please re-run this container with an environment variable \$APP_KEY"
|
||||
echo "An example APP_KEY you could use is: "
|
||||
php artisan key:generate --show
|
||||
/var/www/html/artisan key:generate --show
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -17,8 +17,24 @@ else
|
||||
fi
|
||||
|
||||
# create data directories
|
||||
for dir in 'data/private_uploads' 'data/uploads' 'data/uploads/avatars' 'data/uploads/barcodes' 'data/uploads/categories' 'data/uploads/companies' 'data/uploads/departments' 'data/uploads/locations' 'data/uploads/manufacturers' 'data/uploads/models' 'data/uploads/suppliers' 'dumps' 'keys'; do
|
||||
mkdir -p "/var/lib/snipeit/$dir"
|
||||
for dir in \
|
||||
'data/private_uploads' \
|
||||
'data/uploads/accessories' \
|
||||
'data/uploads/avatars' \
|
||||
'data/uploads/barcodes' \
|
||||
'data/uploads/categories' \
|
||||
'data/uploads/companies' \
|
||||
'data/uploads/components' \
|
||||
'data/uploads/consumables' \
|
||||
'data/uploads/departments' \
|
||||
'data/uploads/locations' \
|
||||
'data/uploads/manufacturers' \
|
||||
'data/uploads/models' \
|
||||
'data/uploads/suppliers' \
|
||||
'dumps' \
|
||||
'keys'
|
||||
do
|
||||
[ ! -d "/var/lib/snipeit/$dir" ] && mkdir -p "/var/lib/snipeit/$dir"
|
||||
done
|
||||
|
||||
chown -R docker:root /var/lib/snipeit/data/*
|
||||
@@ -26,9 +42,9 @@ chown -R docker:root /var/lib/snipeit/dumps
|
||||
chown -R docker:root /var/lib/snipeit/keys
|
||||
|
||||
# If the Oauth DB files are not present copy the vendor files over to the db migrations
|
||||
if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ]; then
|
||||
if [ ! -f "/var/www/html/database/migrations/*create_oauth*" ]
|
||||
then
|
||||
cp -ax /var/www/html/vendor/laravel/passport/database/migrations/* /var/www/html/database/migrations/
|
||||
fi
|
||||
|
||||
. /etc/apache2/envvars
|
||||
exec apache2 -DNO_DETACH < /dev/null
|
||||
exec supervisord -c /supervisord.conf
|
||||
19
docker/supervisor-exit-event-listener
Normal file
19
docker/supervisor-exit-event-listener
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
# A supervisor event listener which terminates supervisord if any of its child
|
||||
# processes enter the FATAL state.
|
||||
# https://stackoverflow.com/a/37527488/119527
|
||||
import os
|
||||
import signal
|
||||
|
||||
from supervisor import childutils
|
||||
|
||||
def main():
|
||||
while True:
|
||||
headers, payload = childutils.listener.wait()
|
||||
childutils.listener.ok()
|
||||
if headers['eventname'] != 'PROCESS_STATE_FATAL':
|
||||
continue
|
||||
os.kill(os.getppid(), signal.SIGTERM)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
27
docker/supervisord.conf
Normal file
27
docker/supervisord.conf
Normal file
@@ -0,0 +1,27 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
|
||||
[program:apache]
|
||||
; https://advancedweb.hu/2018/07/03/supervisor_docker/
|
||||
command=apache2ctl -DFOREGROUND
|
||||
killasgroup=true
|
||||
stopasgroup=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:run_schedule]
|
||||
; Simply run the Laravel command scheduler every minute
|
||||
command=/bin/bash -c "while true; do /var/www/html/artisan schedule:run; sleep 1m; done"
|
||||
user=docker
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
|
||||
; https://stackoverflow.com/a/37527488/119527
|
||||
[eventlistener:exit_on_any_fatal]
|
||||
command=supervisor-exit-event-listener
|
||||
events=PROCESS_STATE_FATAL
|
||||
File diff suppressed because one or more lines are too long
2
public/css/dist/all.css
vendored
2
public/css/dist/all.css
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user