Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fcd805638e | |||
| 63629abb93 | |||
| 6373ef3283 | |||
| 00a7c1e9e2 | |||
| f37d5d3d03 | |||
| 57e52f0ba4 | |||
| dc8e06fc65 | |||
| db3f80bb9b | |||
| f5dda06c55 | |||
| c3166d491a | |||
| 763e17f491 | |||
| 47b2fe571e | |||
| c8f6318aba | |||
| adbb3a8f31 | |||
| 1f142fde8a | |||
| 44eee019d9 | |||
| f636aac2dd | |||
| 54fd1b993b | |||
| a5731a3088 | |||
| 6449d0aaf9 | |||
| 931e2df3bd | |||
| 7f8eddede6 | |||
| 4ddab03792 | |||
| ff341caf34 | |||
| 5754f0aa3f | |||
| 785bc40d9d | |||
| 9d50e0e8d0 | |||
| 9deb4204c8 | |||
| 1ab349a63d | |||
| 2e2d087639 | |||
| bfadb2cea6 | |||
| 44eb67440a | |||
| 8fb97da314 | |||
| 12ff465cdb | |||
| f89d789832 | |||
| 4c3b46ea88 | |||
| 834e0a9dd5 | |||
| 681c41bd18 | |||
| 74488ddceb | |||
| 19820f1b42 | |||
| 2a88781cd5 | |||
| f96c867bd3 | |||
| 06f8e8620a | |||
| 95d907c9e9 | |||
| d990152856 | |||
| 05609230b2 | |||
| 220c254093 | |||
| 02313ce361 | |||
| e70f7c610a | |||
| 32f77c3285 | |||
| 0a639f4fcc | |||
| f2b55fb641 | |||
| 0e443356f0 | |||
| 76f7f01398 | |||
| 992de0156b | |||
| c96b5f5a85 | |||
| 8d2685f0f0 | |||
| eb14cc7f43 | |||
| 3dc67cdba6 | |||
| 0bbe0c85d7 | |||
| 49415806e1 | |||
| 5edbb4b229 | |||
| c40b8334fc | |||
| 0a37c9564a | |||
| 985193ffff | |||
| 721add5bc1 | |||
| ff8fa6ec77 | |||
| e0a6f22489 | |||
| 030fdd60ff | |||
| fdde844ce5 | |||
| d263990401 | |||
| bf7a856fa6 | |||
| 1e062d4fc8 | |||
| ca37de5e45 | |||
| 9ba2fd93c1 | |||
| a2e177e754 | |||
| 5e6db0b219 | |||
| b09ded2a3b | |||
| 124343911f | |||
| 462f8c791f | |||
| 9a224a07ba | |||
| df4686bc96 | |||
| b6c432a596 | |||
| de9f487664 | |||
| ef668317a9 | |||
| cf368a4577 | |||
| 2e71968c04 | |||
| fdb5b3baf1 | |||
| c745fa095b | |||
| 70e6a6ced6 | |||
| 6772835efc | |||
| fb482b0dd6 | |||
| 9f43d3345f | |||
| 6e83679528 | |||
| a050aba72f | |||
| 0031fab0fe | |||
| 585bdff364 | |||
| 1d9741a49e | |||
| 9f7f1460e9 | |||
| f871759753 | |||
| 8e17818f1e | |||
| d19c6ab8e7 | |||
| d14b1e3825 | |||
| ba12ee9954 | |||
| d8bb69533c | |||
| 01d3606c42 | |||
| 208f1db3b2 | |||
| e5b02da54b | |||
| d6ead5ae17 | |||
| 1d7d31b9ae | |||
| 2a817c2123 | |||
| f3a7467235 | |||
| 2da6f9136f | |||
| 79549dbfb9 | |||
| a48d09f37e | |||
| 0dc78fdea6 | |||
| 75a8639a20 | |||
| 380c6171b7 | |||
| d36d6b8e07 | |||
| c00a1fa21b | |||
| bbcd215ea4 | |||
| 444f9a81da | |||
| b4eee5a9b7 | |||
| 72f9fe444d | |||
| eb423c252a | |||
| 382fb31670 | |||
| e6ba4a423d | |||
| 13ed6cde67 | |||
| cac78cdbf3 | |||
| 8b67326e95 | |||
| f65bc5caee | |||
| 0329028e2c | |||
| d3d96c8285 | |||
| 5909860c5a | |||
| 1023fa3edd | |||
| 65e6d56f1f | |||
| bf34385c3e | |||
| 55a526a6b3 | |||
| bbf7fbcff4 | |||
| 4a5cb94d94 | |||
| cb184a9687 | |||
| fb37dbed92 | |||
| e410696a36 | |||
| 45bfec5cd3 | |||
| 055522510b | |||
| f1d0d1bfe7 | |||
| c0aa6c153e | |||
| da3451bf0d | |||
| 5f76e03616 | |||
| 84710eac98 | |||
| 81bf41a091 | |||
| 134acf3b87 | |||
| 82d8b2ab82 | |||
| adc0d3a6ac | |||
| 8b15841c4b | |||
| fd4ee60276 | |||
| 93358b5872 | |||
| 1c4e20c712 | |||
| 0e1f6a3fd1 | |||
| 71d0e6369e | |||
| b27aeb1952 | |||
| bec2b170ec | |||
| 269d3fe509 | |||
| 34d5473553 | |||
| 4ac15daee7 | |||
| 5f3a1f6287 | |||
| bfc60864dd | |||
| ffa2701f89 | |||
| 60d269afb5 | |||
| e2cb7a0242 | |||
| f9b1fdc36b | |||
| 93cf8d4e0a | |||
| e83bc03d97 | |||
| b0d493ee51 | |||
| 4882b01787 | |||
| f9dcf0783a | |||
| 985f3658be | |||
| 705dd34f3e | |||
| 6cf5426540 | |||
| 2105a1ec1d | |||
| f475bdbb2d | |||
| 96eb623229 | |||
| 820a39cc90 | |||
| 615051cf66 | |||
| bef42eb43c | |||
| 6f99ce2b07 | |||
| 76ee5a679b | |||
| 26e4354433 | |||
| 72fc03aa50 | |||
| bf1f8659cb | |||
| c0d7564658 |
+173
-1
@@ -135,7 +135,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10137?v=3",
|
||||
"profile": "https://github.com/ghost",
|
||||
"contributions": [
|
||||
"translation"
|
||||
"translation",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1956,6 +1957,177 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "giannello",
|
||||
"name": "Giuseppe Iannello",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/551789?v=4",
|
||||
"profile": "https://github.com/giannello",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PeterDaveHello",
|
||||
"name": "Peter Dave Hello",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3691490?v=4",
|
||||
"profile": "https://www.peterdavehello.org/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sigmoidal",
|
||||
"name": "sigmoidal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6106332?v=4",
|
||||
"profile": "https://github.com/sigmoidal",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "phenixdotnet",
|
||||
"name": "Vincent Lainé",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2082554?v=4",
|
||||
"profile": "https://github.com/phenixdotnet",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "derlucas",
|
||||
"name": "Lucas Pleß",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1943040?v=4",
|
||||
"profile": "http://www.lucas-pless.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "iansltx",
|
||||
"name": "Ian Littman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/472804?v=4",
|
||||
"profile": "http://twitter.com/iansltx",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PauloLuna",
|
||||
"name": "João Paulo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3519029?v=4",
|
||||
"profile": "https://github.com/PauloLuna",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ThoBur",
|
||||
"name": "ThoBur",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/70443365?v=4",
|
||||
"profile": "https://github.com/ThoBur",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "alek13",
|
||||
"name": "Alexander Chibrikin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1972329?v=4",
|
||||
"profile": "http://phpprofi.ru/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "winstan",
|
||||
"name": "Anthony Winstanley",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/438332?v=4",
|
||||
"profile": "https://github.com/winstan",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fashberg",
|
||||
"name": "Folke",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3075214?v=4",
|
||||
"profile": "https://github.com/fashberg",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benwa",
|
||||
"name": "Bennett Blodinger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1351571?v=4",
|
||||
"profile": "https://github.com/benwa",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ncareau",
|
||||
"name": "NMC",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2974631?v=4",
|
||||
"profile": "https://nmc.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andres-baller",
|
||||
"name": "andres-baller",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/52182449?v=4",
|
||||
"profile": "https://github.com/andres-baller",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sean-borg",
|
||||
"name": "sean-borg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/67109348?v=4",
|
||||
"profile": "https://github.com/sean-borg",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EDVLeer",
|
||||
"name": "EDVLeer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32170051?v=4",
|
||||
"profile": "https://github.com/EDVLeer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Kurokat",
|
||||
"name": "Kurokat",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23075196?v=4",
|
||||
"profile": "https://github.com/Kurokat",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "koelle25",
|
||||
"name": "Kevin Köllmann",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/915514?v=4",
|
||||
"profile": "https://www.kevinkoellmann.de",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sw-mreyes",
|
||||
"name": "sw-mreyes",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/49025941?v=4",
|
||||
"profile": "https://github.com/sw-mreyes",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
|
||||
# Make sure .env files not not browseable if in a sub-directory.
|
||||
<FilesMatch "\.env$">
|
||||
Deny from all
|
||||
# Apache 2.2
|
||||
<IfModule !authz_core_module>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
|
||||
# Apache 2.4+
|
||||
<IfModule authz_core_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
</FilesMatch>
|
||||
|
||||
</IfModule>
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
FROM alpine:3.8
|
||||
FROM alpine:3.12
|
||||
# Apache + PHP
|
||||
RUN apk add --update --no-cache \
|
||||
RUN apk add --no-cache \
|
||||
apache2 \
|
||||
php7 \
|
||||
php7-common \
|
||||
@@ -23,6 +23,8 @@ RUN apk add --update --no-cache \
|
||||
php7-fileinfo \
|
||||
php7-simplexml \
|
||||
php7-session \
|
||||
php7-dom \
|
||||
php7-xmlwriter \
|
||||
curl \
|
||||
wget \
|
||||
vim \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
 [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[](#contributors)
|
||||
[](#contributors)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
@@ -90,7 +90,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/197404?v=3" width="110px;"/><br /><sub>snipe</sub>](http://www.snipe.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snipe "Code") [🚇](#infra-snipe "Infrastructure (Hosting, Build-Tools, etc)") [📖](https://github.com/snipe/snipe-it/commits?author=snipe "Documentation") [⚠️](https://github.com/snipe/snipe-it/commits?author=snipe "Tests") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Asnipe "Bug reports") [🎨](#design-snipe "Design") [👀](#review-snipe "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/36335?v=3" width="110px;"/><br /><sub>Brady Wetherington</sub>](http://www.uberbrady.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=uberbrady "Code") [📖](https://github.com/snipe/snipe-it/commits?author=uberbrady "Documentation") [🚇](#infra-uberbrady "Infrastructure (Hosting, Build-Tools, etc)") [👀](#review-uberbrady "Reviewed Pull Requests") | [<img src="https://avatars0.githubusercontent.com/u/3803132?v=3" width="110px;"/><br /><sub>Daniel Meltzer</sub>](https://github.com/dmeltzer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Tests") [📖](https://github.com/snipe/snipe-it/commits?author=dmeltzer "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1609106?v=3" width="110px;"/><br /><sub>Michael T</sub>](http://www.tuckertechonline.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mtucker6784 "Code") | [<img src="https://avatars2.githubusercontent.com/u/3274937?v=3" width="110px;"/><br /><sub>madd15</sub>](https://github.com/madd15)<br />[📖](https://github.com/snipe/snipe-it/commits?author=madd15 "Documentation") [💬](#question-madd15 "Answering Questions") | [<img src="https://avatars2.githubusercontent.com/u/894126?v=3" width="110px;"/><br /><sub>Vincent Sposato</sub>](https://github.com/vsposato)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vsposato "Code") | [<img src="https://avatars0.githubusercontent.com/u/1639757?v=3" width="110px;"/><br /><sub>Andrea Bergamasco</sub>](https://github.com/vjandrea)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vjandrea "Code") |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/10640152?v=3" width="110px;"/><br /><sub>Karol</sub>](https://github.com/kpawelski)<br />[🌍](#translation-kpawelski "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=kpawelski "Code") | [<img src="https://avatars3.githubusercontent.com/u/600106?v=3" width="110px;"/><br /><sub>morph027</sub>](http://blog.morph027.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=morph027 "Code") | [<img src="https://avatars3.githubusercontent.com/u/22935755?v=3" width="110px;"/><br /><sub>fvleminckx</sub>](https://github.com/fvleminckx)<br />[🚇](#infra-fvleminckx "Infrastructure (Hosting, Build-Tools, etc)") | [<img src="https://avatars2.githubusercontent.com/u/15633547?v=3" width="110px;"/><br /><sub>itsupportcmsukorg</sub>](https://github.com/itsupportcmsukorg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=itsupportcmsukorg "Code") [🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aitsupportcmsukorg "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/12373799?v=3" width="110px;"/><br /><sub>Frank</sub>](https://override.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=base-zero "Code") | [<img src="https://avatars0.githubusercontent.com/u/10137?v=3" width="110px;"/><br /><sub>Deleted user</sub>](https://github.com/ghost)<br />[🌍](#translation-ghost "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=ghost "Code") | [<img src="https://avatars1.githubusercontent.com/u/10802313?v=3" width="110px;"/><br /><sub>tiagom62</sub>](https://github.com/tiagom62)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tiagom62 "Code") [🚇](#infra-tiagom62 "Infrastructure (Hosting, Build-Tools, etc)") |
|
||||
| [<img src="https://avatars3.githubusercontent.com/u/2389047?v=3" width="110px;"/><br /><sub>Ryan Stafford</sub>](https://github.com/rystaf)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rystaf "Code") | [<img src="https://avatars2.githubusercontent.com/u/10345935?v=3" width="110px;"/><br /><sub>Eammon Hanlon</sub>](https://github.com/ehanlon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ehanlon "Code") | [<img src="https://avatars0.githubusercontent.com/u/441924?v=3" width="110px;"/><br /><sub>zjean</sub>](https://github.com/zjean)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zjean "Code") | [<img src="https://avatars0.githubusercontent.com/u/12660103?v=3" width="110px;"/><br /><sub>Matthias Frei</sub>](http://www.frei.media)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FREImedia "Code") | [<img src="https://avatars0.githubusercontent.com/u/3767518?v=3" width="110px;"/><br /><sub>opsydev</sub>](https://github.com/opsydev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=opsydev "Code") | [<img src="https://avatars1.githubusercontent.com/u/82290?v=3" width="110px;"/><br /><sub>Daniel Dreier</sub>](http://www.ddreier.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ddreier "Code") | [<img src="https://avatars0.githubusercontent.com/u/23448?v=3" width="110px;"/><br /><sub>Nikolai Prokoschenko</sub>](http://rassie.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rassie "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/13452757?v=3" width="110px;"/><br /><sub>Drew</sub>](https://github.com/YetAnotherCodeMonkey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=YetAnotherCodeMonkey "Code") | [<img src="https://avatars0.githubusercontent.com/u/1342320?v=3" width="110px;"/><br /><sub>Walter</sub>](https://github.com/merid14)<br />[💻](https://github.com/snipe/snipe-it/commits?author=merid14 "Code") | [<img src="https://avatars3.githubusercontent.com/u/11254614?v=3" width="110px;"/><br /><sub>Petr Baloun</sub>](https://github.com/balous)<br />[💻](https://github.com/snipe/snipe-it/commits?author=balous "Code") | [<img src="https://avatars0.githubusercontent.com/u/6117660?v=3" width="110px;"/><br /><sub>reidblomquist</sub>](https://github.com/reidblomquist)<br />[📖](https://github.com/snipe/snipe-it/commits?author=reidblomquist "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/539914?v=3" width="110px;"/><br /><sub>Mathieu Kooiman</sub>](https://github.com/mathieuk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mathieuk "Code") | [<img src="https://avatars3.githubusercontent.com/u/6606421?v=3" width="110px;"/><br /><sub>csayre</sub>](https://github.com/csayre)<br />[📖](https://github.com/snipe/snipe-it/commits?author=csayre "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/768488?v=3" width="110px;"/><br /><sub>Adam Dunson</sub>](https://github.com/adamdunson)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamdunson "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/5547470?v=3" width="110px;"/><br /><sub>Hereward</sub>](https://github.com/thehereward)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thehereward "Code") | [<img src="https://avatars0.githubusercontent.com/u/5802977?v=3" width="110px;"/><br /><sub>swoopdk</sub>](https://github.com/swoopdk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=swoopdk "Code") | [<img src="https://avatars1.githubusercontent.com/u/3470403?v=3" width="110px;"/><br /><sub>Abdullah Alansari</sub>](https://linkedin.com/in/ahimta)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Ahimta "Code") | [<img src="https://avatars0.githubusercontent.com/u/796443?v=3" width="110px;"/><br /><sub>Micael Rodrigues</sub>](https://github.com/MicaelRodrigues)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MicaelRodrigues "Code") | [<img src="https://avatars0.githubusercontent.com/u/614564?v=3" width="110px;"/><br /><sub>Patrick Gallagher</sub>](http://macadmincorner.com)<br />[📖](https://github.com/snipe/snipe-it/commits?author=patgmac "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/7165922?v=3" width="110px;"/><br /><sub>Miliamber</sub>](https://github.com/Miliamber)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Miliamber "Code") | [<img src="https://avatars3.githubusercontent.com/u/861766?v=3" width="110px;"/><br /><sub>hawk554</sub>](https://github.com/hawk554)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hawk554 "Code") |
|
||||
@@ -119,7 +119,10 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/6609453?v=4" width="110px;"/><br /><sub>Sxderp</sub>](https://github.com/Sxderp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Sxderp "Code") | [<img src="https://avatars1.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>fanta8897</sub>](https://github.com/fanta8897)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fanta8897 "Code") | [<img src="https://avatars2.githubusercontent.com/u/2576509?v=4" width="110px;"/><br /><sub>Andrey Bolonin</sub>](https://andreybolonin.com/phpconsulting/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreybolonin "Code") | [<img src="https://avatars3.githubusercontent.com/u/2173307?v=4" width="110px;"/><br /><sub>shinayoshi</sub>](http://www.shinayoshi.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=shinayoshi "Code") | [<img src="https://avatars3.githubusercontent.com/u/2130159?v=4" width="110px;"/><br /><sub>Hubert</sub>](https://github.com/reuser)<br />[💻](https://github.com/snipe/snipe-it/commits?author=reuser "Code") | [<img src="https://avatars0.githubusercontent.com/u/6865789?v=4" width="110px;"/><br /><sub>KeenRivals</sub>](https://brashear.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KeenRivals "Code") | [<img src="https://avatars3.githubusercontent.com/u/2902513?v=4" width="110px;"/><br /><sub>omyno</sub>](https://github.com/omyno)<br />[💻](https://github.com/snipe/snipe-it/commits?author=omyno "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/6271335?v=4" width="110px;"/><br /><sub>Evgeny</sub>](https://github.com/jackka)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jackka "Code") | [<img src="https://avatars2.githubusercontent.com/u/1169963?v=4" width="110px;"/><br /><sub>Colin Campbell</sub>](https://digitalist.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=colin-campbell "Code") | [<img src="https://avatars3.githubusercontent.com/u/2872098?v=4" width="110px;"/><br /><sub>Ľubomír Kučera</sub>](https://github.com/lubo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lubo "Code") | [<img src="https://avatars3.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://www.sourceguru.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mezzle "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") | [<img src="https://avatars0.githubusercontent.com/u/17459600?v=4" width="110px;"/><br /><sub>Marián Skrip</sub>](https://github.com/mskrip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") | [<img src="https://avatars2.githubusercontent.com/u/47435081?v=4" width="110px;"/><br /><sub>Godfrey Martinez</sub>](https://github.com/Godmartinz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/2075128?v=4" width="110px;"/><br /><sub>bigtreeEdo</sub>](https://github.com/bigtreeEdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [<img src="https://avatars0.githubusercontent.com/u/5000430?v=4" width="110px;"/><br /><sub>Colin McNeil</sub>](https://colinmcneil.me/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [<img src="https://avatars0.githubusercontent.com/u/421625?v=4" width="110px;"/><br /><sub>JoKneeMo</sub>](https://github.com/JoKneeMo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [<img src="https://avatars0.githubusercontent.com/u/54849013?v=4" width="110px;"/><br /><sub>Joshi</sub>](http://www.redbridge.se)<br />[💻](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [<img src="https://avatars2.githubusercontent.com/u/15731458?v=4" width="110px;"/><br /><sub>Anthony Burns</sub>](https://github.com/anthonypburns)<br />[💻](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [<img src="https://avatars1.githubusercontent.com/u/63399474?v=4" width="110px;"/><br /><sub>johnson-yi</sub>](https://github.com/johnson-yi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=johnson-yi "Code") | [<img src="https://avatars1.githubusercontent.com/u/1862720?v=4" width="110px;"/><br /><sub>Sanjay Govind</sub>](https://tangentmc.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sanjay900 "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") | [<img src="https://avatars1.githubusercontent.com/u/1733625?v=4" width="110px;"/><br /><sub>Dampfklon</sub>](https://github.com/dampfklon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dampfklon "Code") | [<img src="https://avatars2.githubusercontent.com/u/52973156?v=4" width="110px;"/><br /><sub>Charles Hamilton</sub>](https://communityclosing.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chamilton-ccn "Code") |
|
||||
| [<img src="https://avatars0.githubusercontent.com/u/1255375?v=4" width="110px;"/><br /><sub>Peter Upfold</sub>](https://peter.upfold.org.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterUpfold "Code") | [<img src="https://avatars2.githubusercontent.com/u/961717?v=4" width="110px;"/><br /><sub>Jared Biel</sub>](https://github.com/jbiel)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jbiel "Code") | [<img src="https://avatars1.githubusercontent.com/u/1733625?v=4" width="110px;"/><br /><sub>Dampfklon</sub>](https://github.com/dampfklon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dampfklon "Code") | [<img src="https://avatars2.githubusercontent.com/u/52973156?v=4" width="110px;"/><br /><sub>Charles Hamilton</sub>](https://communityclosing.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chamilton-ccn "Code") | [<img src="https://avatars.githubusercontent.com/u/551789?v=4" width="110px;"/><br /><sub>Giuseppe Iannello</sub>](https://github.com/giannello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=giannello "Code") | [<img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="110px;"/><br /><sub>Peter Dave Hello</sub>](https://www.peterdavehello.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PeterDaveHello "Code") | [<img src="https://avatars.githubusercontent.com/u/6106332?v=4" width="110px;"/><br /><sub>sigmoidal</sub>](https://github.com/sigmoidal)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sigmoidal "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/2082554?v=4" width="110px;"/><br /><sub>Vincent Lainé</sub>](https://github.com/phenixdotnet)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phenixdotnet "Code") | [<img src="https://avatars.githubusercontent.com/u/1943040?v=4" width="110px;"/><br /><sub>Lucas Pleß</sub>](http://www.lucas-pless.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derlucas "Code") | [<img src="https://avatars.githubusercontent.com/u/472804?v=4" width="110px;"/><br /><sub>Ian Littman</sub>](http://twitter.com/iansltx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=iansltx "Code") | [<img src="https://avatars.githubusercontent.com/u/3519029?v=4" width="110px;"/><br /><sub>João Paulo</sub>](https://github.com/PauloLuna)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PauloLuna "Code") | [<img src="https://avatars.githubusercontent.com/u/70443365?v=4" width="110px;"/><br /><sub>ThoBur</sub>](https://github.com/ThoBur)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ThoBur "Code") | [<img src="https://avatars.githubusercontent.com/u/1972329?v=4" width="110px;"/><br /><sub>Alexander Chibrikin</sub>](http://phpprofi.ru/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") | [<img src="https://avatars.githubusercontent.com/u/438332?v=4" width="110px;"/><br /><sub>Anthony Winstanley</sub>](https://github.com/winstan)<br />[💻](https://github.com/snipe/snipe-it/commits?author=winstan "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/3075214?v=4" width="110px;"/><br /><sub>Folke</sub>](https://github.com/fashberg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fashberg "Code") | [<img src="https://avatars.githubusercontent.com/u/1351571?v=4" width="110px;"/><br /><sub>Bennett Blodinger</sub>](https://github.com/benwa)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benwa "Code") | [<img src="https://avatars.githubusercontent.com/u/2974631?v=4" width="110px;"/><br /><sub>NMC</sub>](https://nmc.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ncareau "Code") | [<img src="https://avatars.githubusercontent.com/u/52182449?v=4" width="110px;"/><br /><sub>andres-baller</sub>](https://github.com/andres-baller)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andres-baller "Code") | [<img src="https://avatars.githubusercontent.com/u/67109348?v=4" width="110px;"/><br /><sub>sean-borg</sub>](https://github.com/sean-borg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sean-borg "Code") | [<img src="https://avatars.githubusercontent.com/u/32170051?v=4" width="110px;"/><br /><sub>EDVLeer</sub>](https://github.com/EDVLeer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=EDVLeer "Code") | [<img src="https://avatars.githubusercontent.com/u/23075196?v=4" width="110px;"/><br /><sub>Kurokat</sub>](https://github.com/Kurokat)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Kurokat "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/915514?v=4" width="110px;"/><br /><sub>Kevin Köllmann</sub>](https://www.kevinkoellmann.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koelle25 "Code") | [<img src="https://avatars.githubusercontent.com/u/49025941?v=4" width="110px;"/><br /><sub>sw-mreyes</sub>](https://github.com/sw-mreyes)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sw-mreyes "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -88,11 +88,13 @@ class ImportLocations extends Command
|
||||
|
||||
if (array_key_exists('Parent Name', $row)) {
|
||||
$parent_name = trim($row['Parent Name']);
|
||||
} else {
|
||||
$parent_name = null;
|
||||
}
|
||||
|
||||
// Set the location attributes to save
|
||||
if (array_key_exists('Name', $row)) {
|
||||
$location = Location::firstOrNew(array('name' => trim($row['Name'])));
|
||||
$location = Location::firstOrCreate(array('name' => trim($row['Name'])));
|
||||
$location->name = trim($row['Name']);
|
||||
$this->info('Checking location: '.$location->name);
|
||||
} else {
|
||||
|
||||
Regular → Executable
+225
-325
@@ -1,24 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Log;
|
||||
use Exception;
|
||||
use App\Models\User;
|
||||
use App\Services\LdapAd;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Console\Command;
|
||||
use Adldap\Models\User as AdldapUser;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Ldap;
|
||||
use App\Models\User;
|
||||
use App\Models\Location;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* LDAP / AD sync command.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class LdapSync extends Command
|
||||
{
|
||||
/**
|
||||
@@ -26,79 +16,23 @@ class LdapSync extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:ldap-sync
|
||||
{--location= : A location name }
|
||||
{--location_id= : A location id}
|
||||
{--base_dn= : A diffrent base DN to use }
|
||||
{--summary : Print summary }
|
||||
{--json_summary : Print summary in json format }
|
||||
{--dryrun : Run the sync process but don\'t update the database}';
|
||||
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command line LDAP/AD sync';
|
||||
|
||||
/**
|
||||
* An LdapAd instance.
|
||||
*
|
||||
* @var \App\Models\LdapAd
|
||||
*/
|
||||
private $ldap;
|
||||
|
||||
/**
|
||||
* LDAP settings collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $settings = null;
|
||||
|
||||
/**
|
||||
* A default location collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $defaultLocation = null;
|
||||
|
||||
/**
|
||||
* Mapped locations collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $mappedLocations = null;
|
||||
|
||||
/**
|
||||
* The summary collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $summary;
|
||||
|
||||
/**
|
||||
* Is dry-run?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $dryrun = false;
|
||||
|
||||
/**
|
||||
* Show users to be imported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $userlist = [];
|
||||
protected $description = 'Command line LDAP sync';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(LdapAd $ldap)
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->ldap = $ldap;
|
||||
$this->settings = $this->ldap->ldapSettings;
|
||||
$this->summary = collect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,275 +42,241 @@ class LdapSync extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', env('LDAP_TIME_LIM', "600")); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', '500M');
|
||||
$old_error_reporting = error_reporting(); // grab old error_reporting .ini setting, for later re-enablement
|
||||
error_reporting($old_error_reporting & ~E_DEPRECATED); // disable deprecation warnings, for LDAP in PHP 7.4 (and greater)
|
||||
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
|
||||
$ldap_result_username = Setting::getSettings()->ldap_username_field;
|
||||
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
|
||||
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
|
||||
|
||||
if ($this->option('dryrun')) {
|
||||
$this->dryrun = true;
|
||||
}
|
||||
$this->checkIfLdapIsEnabled();
|
||||
$this->checkLdapConnection();
|
||||
$this->setBaseDn();
|
||||
$this->getUserDefaultLocation();
|
||||
/*
|
||||
* Use the default location if set, this is needed for the LDAP users sync page
|
||||
*/
|
||||
if (!$this->option('base_dn') && null == $this->defaultLocation) {
|
||||
$this->getMappedLocations();
|
||||
}
|
||||
$this->processLdapUsers();
|
||||
// Print table of users
|
||||
if ($this->dryrun) {
|
||||
$this->info('The following users will be synced!');
|
||||
$headers = ['First Name', 'Last Name', 'Username', 'Email', 'Employee #', 'Location Id', 'Status'];
|
||||
$this->table($headers, $this->summary->toArray());
|
||||
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
|
||||
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
|
||||
$ldap_result_email = Setting::getSettings()->ldap_email;
|
||||
|
||||
try {
|
||||
$ldapconn = Ldap::connectToLdap();
|
||||
Ldap::bindAdminToLdap($ldapconn);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
error_reporting($old_error_reporting); // re-enable deprecation warnings.
|
||||
return $this->getSummary();
|
||||
}
|
||||
$summary = array();
|
||||
|
||||
/**
|
||||
* Generate the LDAP sync summary.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSummary(): string
|
||||
{
|
||||
if ($this->option('summary') && null === $this->dryrun) {
|
||||
$this->summary->each(function ($item) {
|
||||
$this->info('USER: '.$item['note']);
|
||||
|
||||
if ('ERROR' === $item['status']) {
|
||||
$this->error('ERROR: '.$item['note']);
|
||||
}
|
||||
});
|
||||
} elseif ($this->option('json_summary')) {
|
||||
$json_summary = [
|
||||
'error' => false,
|
||||
'error_message' => '',
|
||||
'summary' => $this->summary->toArray(),
|
||||
];
|
||||
$this->info(json_encode($json_summary));
|
||||
try {
|
||||
if ($this->option('base_dn') != '') {
|
||||
$search_base = $this->option('base_dn');
|
||||
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
|
||||
} else {
|
||||
$search_base = null;
|
||||
}
|
||||
$results = Ldap::findLdapUsers($search_base);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = NULL; // FIXME - this would be better called "$default_location", which is more explicit about its purpose
|
||||
|
||||
/**
|
||||
* Create a new user or update an existing user.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param \Adldap\Models\User $snipeUser
|
||||
*/
|
||||
private function updateCreateUser(AdldapUser $snipeUser): void
|
||||
{
|
||||
$user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations);
|
||||
$summary = [
|
||||
'firstname' => $user->first_name,
|
||||
'lastname' => $user->last_name,
|
||||
'username' => $user->username,
|
||||
'employee_number' => $user->employee_num,
|
||||
'email' => $user->email,
|
||||
'location_id' => $user->location_id,
|
||||
];
|
||||
// Only update the database if is not a dry run
|
||||
if (!$this->dryrun) {
|
||||
if ($user->isDirty()) { //if nothing on the user changed, don't bother trying to save anything nor put anything in the summary
|
||||
if ($user->save()) {
|
||||
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED');
|
||||
$summary['status'] = 'SUCCESS';
|
||||
} else {
|
||||
$errors = '';
|
||||
foreach ($user->getErrors()->getMessages() as $error) {
|
||||
$errors .= implode(", ",$error);
|
||||
if ($this->option('location')!='') {
|
||||
$location = Location::where('name', '=', $this->option('location'))->first();
|
||||
LOG::debug('Location name '.$this->option('location').' passed');
|
||||
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
} elseif ($this->option('location_id')!='') {
|
||||
$location = Location::where('id', '=', $this->option('location_id'))->first();
|
||||
LOG::debug('Location ID '.$this->option('location_id').' passed');
|
||||
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
|
||||
}
|
||||
|
||||
if (!isset($location)) {
|
||||
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
/* Process locations with explicitly defined OUs, if doing a full import. */
|
||||
if ($this->option('base_dn')=='') {
|
||||
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
|
||||
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
|
||||
$ldap_ou_lengths = array();
|
||||
|
||||
foreach ($ldap_ou_locations as $ou_loc) {
|
||||
$ldap_ou_lengths[] = strlen($ou_loc["ldap_ou"]);
|
||||
}
|
||||
|
||||
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
|
||||
|
||||
if (sizeof($ldap_ou_locations) > 0) {
|
||||
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
|
||||
}
|
||||
|
||||
// Inject location information fields
|
||||
for ($i = 0; $i < $results["count"]; $i++) {
|
||||
$results[$i]["ldap_location_override"] = false;
|
||||
$results[$i]["location_id"] = 0;
|
||||
}
|
||||
|
||||
// Grab subsets based on location-specific DNs, and overwrite location for these users.
|
||||
foreach ($ldap_ou_locations as $ldap_loc) {
|
||||
try {
|
||||
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
|
||||
} catch (\Exception $e) { // FIXME: this is stolen from line 77 or so above
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => true, "error_message" => trans('admin/users/message.error.ldap_could_not_search')." Location: ".$ldap_loc['name']." (ID: ".$ldap_loc['id'].") cannot connect to \"".$ldap_loc["ldap_ou"]."\" - ".$e->getMessage(), "summary" => [] ];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
$summary['note'] = $snipeUser->getDN().' was not imported. REASON: '.$errors;
|
||||
$summary['status'] = 'ERROR';
|
||||
LOG::info($e);
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
$summary = null;
|
||||
}
|
||||
}
|
||||
$usernames = array();
|
||||
for ($i = 0; $i < $location_users["count"]; $i++) {
|
||||
|
||||
// $summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); // this seems, kinda, like, superfluous, relative to the $summary['note'] thing above, yeah?
|
||||
if($summary) { //if the $user wasn't dirty, $summary was set to null so that we will skip the following push()
|
||||
$this->summary->push($summary);
|
||||
}
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the users to update / create.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
*/
|
||||
private function processLdapUsers(): void
|
||||
{
|
||||
try {
|
||||
$ldapUsers = $this->ldap->getLdapUsers();
|
||||
} catch (Exception $e) {
|
||||
$this->outputError($e);
|
||||
exit($e->getMessage());
|
||||
}
|
||||
|
||||
if (0 == $ldapUsers->count()) {
|
||||
$msg = 'ERROR: No users found!';
|
||||
Log::error($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->error($msg);
|
||||
}
|
||||
exit($msg);
|
||||
}
|
||||
|
||||
// Process each individual users
|
||||
foreach ($ldapUsers->getResults() as $user) { // AdLdap2's paginate() method is weird, it gets *everything* and ->getResults() returns *everything*
|
||||
$this->updateCreateUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapped locations if a base_dn is provided.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function getMappedLocations()
|
||||
{
|
||||
$ldapOuLocation = Location::where('ldap_ou', '!=', '')->select(['id', 'ldap_ou'])->get();
|
||||
$locations = $ldapOuLocation->sortBy(function ($ou, $key) {
|
||||
return strlen($ou->ldap_ou);
|
||||
});
|
||||
if ($locations->count() > 0) {
|
||||
$msg = 'Some locations have special OUs set. Locations will be automatically set for users in those OUs.';
|
||||
LOG::debug($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
|
||||
$this->mappedLocations = $locations->pluck('ldap_ou', 'id'); // TODO: this seems ok-ish, but the key-> value is going location_id -> OU name, and the primary action here is the opposite of that - going from OU's to location ID's.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base dn if supplied.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function setBaseDn(): void
|
||||
{
|
||||
if ($this->option('base_dn')) {
|
||||
$this->ldap->baseDn = $this->option('base_dn');
|
||||
$msg = sprintf('Importing users from specified base DN: "%s"', $this->ldap->baseDn);
|
||||
LOG::debug($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a default location id for imported users.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function getUserDefaultLocation(): void
|
||||
{
|
||||
$location = $this->option('location_id') ?? $this->option('location');
|
||||
if ($location) {
|
||||
$userLocation = Location::where('name', '=', $location)
|
||||
->orWhere('id', '=', intval($location))
|
||||
->select(['name', 'id'])
|
||||
->first();
|
||||
if ($userLocation) {
|
||||
$msg = 'Importing users with default location: '.$userLocation->name.' ('.$userLocation->id.')';
|
||||
LOG::debug($msg);
|
||||
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
|
||||
$this->defaultLocation = collect([
|
||||
$userLocation->id => $userLocation->name,
|
||||
]);
|
||||
} else {
|
||||
$msg = 'The supplied location is invalid!';
|
||||
LOG::error($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->error($msg);
|
||||
// Delete located users from the general group.
|
||||
foreach ($results as $key => $generic_entry) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
|
||||
$global_count = $results['count'];
|
||||
$results = array_merge($location_users, $results);
|
||||
$results['count'] = $global_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if LDAP intergration is enabled.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function checkIfLdapIsEnabled(): void
|
||||
{
|
||||
if (false === $this->settings['ldap_enabled']) {
|
||||
$msg = 'LDAP intergration is not enabled. Exiting sync process.';
|
||||
$this->info($msg);
|
||||
Log::info($msg);
|
||||
exit(0);
|
||||
/* Create user account entries in Snipe-IT */
|
||||
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
$pass = bcrypt($tmp_pass);
|
||||
|
||||
for ($i = 0; $i < $results["count"]; $i++) {
|
||||
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
|
||||
|
||||
$item = array();
|
||||
$item["username"] = isset($results[$i][$ldap_result_username][0]) ? $results[$i][$ldap_result_username][0] : "";
|
||||
$item["employee_number"] = isset($results[$i][$ldap_result_emp_num][0]) ? $results[$i][$ldap_result_emp_num][0] : "";
|
||||
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
|
||||
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
|
||||
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
|
||||
$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"]:"";
|
||||
|
||||
$user = User::where('username', $item["username"])->first();
|
||||
if ($user) {
|
||||
// Updating an existing user.
|
||||
$item["createorupdate"] = 'updated';
|
||||
} else {
|
||||
// Creating a new user.
|
||||
$user = new User;
|
||||
$user->password = $pass;
|
||||
$user->activated = 0;
|
||||
$item["createorupdate"] = 'created';
|
||||
}
|
||||
|
||||
$user->first_name = $item["firstname"];
|
||||
$user->last_name = $item["lastname"];
|
||||
$user->username = $item["username"];
|
||||
$user->email = $item["email"];
|
||||
$user->employee_num = e($item["employee_number"]);
|
||||
|
||||
// Sync activated state for Active Directory.
|
||||
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
|
||||
/* The following is _probably_ the correct logic, but we can't use it because
|
||||
some users may have been dependent upon the previous behavior, and this
|
||||
could cause additional access to be available to users they don't want
|
||||
to allow to log in.
|
||||
|
||||
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
|
||||
if(
|
||||
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
|
||||
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
|
||||
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
|
||||
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
|
||||
) {
|
||||
$user->activated = 1;
|
||||
} else {
|
||||
$user->activated = 0;
|
||||
} */
|
||||
$enabled_accounts = [
|
||||
'512', // 0x200 NORMAL_ACCOUNT
|
||||
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
|
||||
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
|
||||
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
|
||||
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
|
||||
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
|
||||
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'4260352',// 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
|
||||
'1049088',// 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
|
||||
];
|
||||
$user->activated = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
// If we're not using AD, and there isn't an activated flag set, activate all users
|
||||
elseif (empty($ldap_result_active_flag)) {
|
||||
$user->activated = 1;
|
||||
}
|
||||
|
||||
if ($item['ldap_location_override'] == true) {
|
||||
$user->location_id = $item['location_id'];
|
||||
} elseif ((isset($location)) && (!empty($location))) {
|
||||
|
||||
if ((is_array($location)) && (array_key_exists('id', $location))) {
|
||||
$user->location_id = $location['id'];
|
||||
} elseif (is_object($location)) {
|
||||
$user->location_id = $location->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$user->ldap_import = 1;
|
||||
|
||||
$errors = '';
|
||||
|
||||
if ($user->save()) {
|
||||
$item["note"] = $item["createorupdate"];
|
||||
$item["status"]='success';
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
$errors .= $err[0];
|
||||
}
|
||||
$item["note"] = $errors;
|
||||
$item["status"]='error';
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure we can access the server.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function checkLdapConnection(): void
|
||||
{
|
||||
try {
|
||||
$this->ldap->testLdapAdUserConnection();
|
||||
$this->ldap->testLdapAdBindConnection();
|
||||
} catch (Exception $e) {
|
||||
$this->outputError($e);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the json summary to the screen if enabled.
|
||||
*
|
||||
* @param Exception $error
|
||||
*/
|
||||
private function outputError($error): void
|
||||
{
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [
|
||||
'error' => true,
|
||||
'error_message' => $error->getMessage(),
|
||||
'summary' => [],
|
||||
];
|
||||
if ($this->option('summary')) {
|
||||
for ($x = 0; $x < count($summary); $x++) {
|
||||
if ($summary[$x]['status']=='error') {
|
||||
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
|
||||
} else {
|
||||
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
|
||||
}
|
||||
}
|
||||
} else if ($this->option('json_summary')) {
|
||||
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ]; // hardcoding the error to false and the error_message to blank seems a bit weird
|
||||
$this->info(json_encode($json_summary));
|
||||
} else {
|
||||
return $summary;
|
||||
}
|
||||
$this->error($error->getMessage());
|
||||
LOG::error($error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Log;
|
||||
use Exception;
|
||||
use App\Models\User;
|
||||
use App\Services\LdapAd;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Console\Command;
|
||||
use Adldap\Models\User as AdldapUser;
|
||||
|
||||
/**
|
||||
* LDAP / AD sync command.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class LdapSyncNg extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:ldap-sync-ng
|
||||
{--location= : A location name }
|
||||
{--location_id= : A location id}
|
||||
{--base_dn= : A diffrent base DN to use }
|
||||
{--summary : Print summary }
|
||||
{--json_summary : Print summary in json format }
|
||||
{--dryrun : Run the sync process but don\'t update the database}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command line LDAP/AD sync';
|
||||
|
||||
/**
|
||||
* An LdapAd instance.
|
||||
*
|
||||
* @var \App\Models\LdapAd
|
||||
*/
|
||||
private $ldap;
|
||||
|
||||
/**
|
||||
* LDAP settings collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $settings = null;
|
||||
|
||||
/**
|
||||
* A default location collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $defaultLocation = null;
|
||||
|
||||
/**
|
||||
* Mapped locations collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $mappedLocations = null;
|
||||
|
||||
/**
|
||||
* The summary collection.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection
|
||||
*/
|
||||
private $summary;
|
||||
|
||||
/**
|
||||
* Is dry-run?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $dryrun = false;
|
||||
|
||||
/**
|
||||
* Show users to be imported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $userlist = [];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*/
|
||||
public function __construct(LdapAd $ldap)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->ldap = $ldap;
|
||||
$this->settings = $this->ldap->ldapSettings;
|
||||
$this->summary = collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$dispatcher = \Adldap\Adldap::getEventDispatcher();
|
||||
|
||||
// Listen for all model events.
|
||||
$dispatcher->listen('Adldap\Models\Events\*', function ($eventName, array $data) {
|
||||
echo $eventName; // Returns 'Adldap\Models\Events\Updating'
|
||||
var_dump($data); // Returns [0] => (object) Adldap\Models\Events\Updating;
|
||||
\Log::debug("Event: ".$eventName." data - ".print_r($data, true));
|
||||
});
|
||||
$dispatcher->listen('Adldap\Auth\Events\*', function ($eventName, array $data) {
|
||||
echo $eventName; // Returns 'Adldap\Models\Events\Updating'
|
||||
var_dump($data); // Returns [0] => (object) Adldap\Models\Events\Updating;
|
||||
\Log::debug("Event: ".$eventName." data - ".print_r($data, true));
|
||||
});
|
||||
|
||||
ini_set('max_execution_time', env('LDAP_TIME_LIM', "600")); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', '500M');
|
||||
$old_error_reporting = error_reporting(); // grab old error_reporting .ini setting, for later re-enablement
|
||||
error_reporting($old_error_reporting & ~E_DEPRECATED); // disable deprecation warnings, for LDAP in PHP 7.4 (and greater)
|
||||
|
||||
if ($this->option('dryrun')) {
|
||||
$this->dryrun = true;
|
||||
}
|
||||
$this->checkIfLdapIsEnabled();
|
||||
$this->checkLdapConnection();
|
||||
$this->setBaseDn();
|
||||
$this->getUserDefaultLocation();
|
||||
/*
|
||||
* Use the default location if set, this is needed for the LDAP users sync page
|
||||
*/
|
||||
if (!$this->option('base_dn') && null == $this->defaultLocation) {
|
||||
$this->getMappedLocations();
|
||||
}
|
||||
$this->processLdapUsers();
|
||||
// Print table of users
|
||||
if ($this->dryrun) {
|
||||
$this->info('The following users will be synced!');
|
||||
$headers = ['First Name', 'Last Name', 'Username', 'Email', 'Employee #', 'Location Id', 'Status'];
|
||||
$this->table($headers, $this->summary->toArray());
|
||||
}
|
||||
|
||||
error_reporting($old_error_reporting); // re-enable deprecation warnings.
|
||||
return $this->getSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the LDAP sync summary.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSummary(): string
|
||||
{
|
||||
if ($this->option('summary') && null === $this->dryrun) {
|
||||
$this->summary->each(function ($item) {
|
||||
$this->info('USER: '.$item['note']);
|
||||
|
||||
if ('ERROR' === $item['status']) {
|
||||
$this->error('ERROR: '.$item['note']);
|
||||
}
|
||||
});
|
||||
} elseif ($this->option('json_summary')) {
|
||||
$json_summary = [
|
||||
'error' => false,
|
||||
'error_message' => '',
|
||||
'summary' => $this->summary->toArray(),
|
||||
];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user or update an existing user.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @param \Adldap\Models\User $snipeUser
|
||||
*/
|
||||
private function updateCreateUser(AdldapUser $snipeUser): void
|
||||
{
|
||||
$user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations);
|
||||
$summary = [
|
||||
'firstname' => $user->first_name,
|
||||
'lastname' => $user->last_name,
|
||||
'username' => $user->username,
|
||||
'employee_number' => $user->employee_num,
|
||||
'email' => $user->email,
|
||||
'location_id' => $user->location_id,
|
||||
];
|
||||
// Only update the database if is not a dry run
|
||||
if (!$this->dryrun) {
|
||||
if ($user->isDirty()) { //if nothing on the user changed, don't bother trying to save anything nor put anything in the summary
|
||||
if ($user->save()) {
|
||||
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED');
|
||||
$summary['status'] = 'SUCCESS';
|
||||
} else {
|
||||
$errors = '';
|
||||
foreach ($user->getErrors()->getMessages() as $error) {
|
||||
$errors .= implode(", ",$error);
|
||||
}
|
||||
$summary['note'] = $snipeUser->getDN().' was not imported. REASON: '.$errors;
|
||||
$summary['status'] = 'ERROR';
|
||||
}
|
||||
} else {
|
||||
$summary = null;
|
||||
}
|
||||
}
|
||||
|
||||
// $summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); // this seems, kinda, like, superfluous, relative to the $summary['note'] thing above, yeah?
|
||||
if($summary) { //if the $user wasn't dirty, $summary was set to null so that we will skip the following push()
|
||||
$this->summary->push($summary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the users to update / create.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
*/
|
||||
private function processLdapUsers(): void
|
||||
{
|
||||
try {
|
||||
\Log::debug("CAL:LING GET LDAP SUSERS");
|
||||
$ldapUsers = $this->ldap->getLdapUsers();
|
||||
\Log::debug("END CALLING GET LDAP USERS");
|
||||
} catch (Exception $e) {
|
||||
$this->outputError($e);
|
||||
exit($e->getMessage());
|
||||
}
|
||||
|
||||
if (0 == $ldapUsers->count()) {
|
||||
$msg = 'ERROR: No users found!';
|
||||
Log::error($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->error($msg);
|
||||
}
|
||||
exit($msg);
|
||||
}
|
||||
|
||||
// Process each individual users
|
||||
foreach ($ldapUsers->getResults() as $user) { // AdLdap2's paginate() method is weird, it gets *everything* and ->getResults() returns *everything*
|
||||
$this->updateCreateUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapped locations if a base_dn is provided.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function getMappedLocations()
|
||||
{
|
||||
$ldapOuLocation = Location::where('ldap_ou', '!=', '')->select(['id', 'ldap_ou'])->get();
|
||||
$locations = $ldapOuLocation->sortBy(function ($ou, $key) {
|
||||
return strlen($ou->ldap_ou);
|
||||
});
|
||||
if ($locations->count() > 0) {
|
||||
$msg = 'Some locations have special OUs set. Locations will be automatically set for users in those OUs.';
|
||||
LOG::debug($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
|
||||
$this->mappedLocations = $locations->pluck('ldap_ou', 'id'); // TODO: this seems ok-ish, but the key-> value is going location_id -> OU name, and the primary action here is the opposite of that - going from OU's to location ID's.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base dn if supplied.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function setBaseDn(): void
|
||||
{
|
||||
if ($this->option('base_dn')) {
|
||||
$this->ldap->baseDn = $this->option('base_dn');
|
||||
$msg = sprintf('Importing users from specified base DN: "%s"', $this->ldap->baseDn);
|
||||
LOG::debug($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a default location id for imported users.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function getUserDefaultLocation(): void
|
||||
{
|
||||
$location = $this->option('location_id') ?? $this->option('location');
|
||||
if ($location) {
|
||||
$userLocation = Location::where('name', '=', $location)
|
||||
->orWhere('id', '=', intval($location))
|
||||
->select(['name', 'id'])
|
||||
->first();
|
||||
if ($userLocation) {
|
||||
$msg = 'Importing users with default location: '.$userLocation->name.' ('.$userLocation->id.')';
|
||||
LOG::debug($msg);
|
||||
|
||||
if ($this->dryrun) {
|
||||
$this->info($msg);
|
||||
}
|
||||
|
||||
$this->defaultLocation = collect([
|
||||
$userLocation->id => $userLocation->name,
|
||||
]);
|
||||
} else {
|
||||
$msg = 'The supplied location is invalid!';
|
||||
LOG::error($msg);
|
||||
if ($this->dryrun) {
|
||||
$this->error($msg);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if LDAP intergration is enabled.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function checkIfLdapIsEnabled(): void
|
||||
{
|
||||
if (false === $this->settings['ldap_enabled']) {
|
||||
$msg = 'LDAP intergration is not enabled. Exiting sync process.';
|
||||
$this->info($msg);
|
||||
Log::info($msg);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to make sure we can access the server.
|
||||
*
|
||||
* @author Wes Hulette <jwhulette@gmail.com>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private function checkLdapConnection(): void
|
||||
{
|
||||
try {
|
||||
$this->ldap->testLdapAdUserConnection();
|
||||
$this->ldap->testLdapAdBindConnection();
|
||||
} catch (Exception $e) {
|
||||
$this->outputError($e);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the json summary to the screen if enabled.
|
||||
*
|
||||
* @param Exception $error
|
||||
*/
|
||||
private function outputError($error): void
|
||||
{
|
||||
if ($this->option('json_summary')) {
|
||||
$json_summary = [
|
||||
'error' => true,
|
||||
'error_message' => $error->getMessage(),
|
||||
'summary' => [],
|
||||
];
|
||||
$this->info(json_encode($json_summary));
|
||||
}
|
||||
$this->error($error->getMessage());
|
||||
LOG::error($error);
|
||||
}
|
||||
}
|
||||
@@ -66,9 +66,6 @@ class ResetDemoSettings extends Command
|
||||
$settings->version_footer = 'on';
|
||||
$settings->support_footer = null;
|
||||
$settings->saml_enabled = '0';
|
||||
$settings->saml_sp_entitiyid = '0';
|
||||
$settings->saml_sp_acs_url = null;
|
||||
$settings->saml_sp_sls_url = null;
|
||||
$settings->saml_sp_x509cert = null;
|
||||
$settings->saml_idp_metadata = null;
|
||||
$settings->saml_attr_mapping_username = null;
|
||||
@@ -84,7 +81,7 @@ class ResetDemoSettings extends Command
|
||||
$user->save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Notifications\ExpectedCheckinAdminNotification;
|
||||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Recipients\AlertRecipient;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ class Handler extends ExceptionHandler
|
||||
\Illuminate\Session\TokenMismatchException::class,
|
||||
\Illuminate\Validation\ValidationException::class,
|
||||
\Intervention\Image\Exception\NotSupportedException::class,
|
||||
\League\OAuth2\Server\Exception\OAuthServerException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -105,7 +106,7 @@ class Handler extends ExceptionHandler
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['error' => 'Unauthorized.'], 401);
|
||||
return response()->json(['error' => 'Unauthorized or unauthenticated.'], 401);
|
||||
}
|
||||
|
||||
return redirect()->guest('login');
|
||||
|
||||
@@ -428,6 +428,26 @@ class Helper
|
||||
return $statuslabel_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of deployable status labels in an array to make a dropdown menu
|
||||
*
|
||||
* @todo This should probably be a selectlist, same as the other endpoints
|
||||
* and we should probably add to the API controllers to make sure that
|
||||
* the status_id submitted is actually really deployable.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.1.0]
|
||||
* @return Array
|
||||
*/
|
||||
public static function deployableStatusLabelList()
|
||||
{
|
||||
$statuslabel_list = Statuslabel::where('deployable', '=', '1')->orderBy('default_label', 'desc')
|
||||
->orderBy('name','asc')
|
||||
->orderBy('deployable','desc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
return $statuslabel_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of status label types in an array to make a dropdown menu
|
||||
*
|
||||
|
||||
@@ -30,7 +30,20 @@ class AssetModelsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', AssetModel::class);
|
||||
$allowed_columns = ['id','image','name','model_number','eol','notes','created_at','manufacturer','requestable', 'assets_count'];
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'image',
|
||||
'name',
|
||||
'model_number',
|
||||
'eol',
|
||||
'notes',
|
||||
'created_at',
|
||||
'manufacturer',
|
||||
'requestable',
|
||||
'assets_count',
|
||||
'category'
|
||||
];
|
||||
|
||||
$assetmodels = AssetModel::select([
|
||||
'models.id',
|
||||
@@ -75,13 +88,14 @@ class AssetModelsController extends Controller
|
||||
case 'manufacturer':
|
||||
$assetmodels->OrderManufacturer($order);
|
||||
break;
|
||||
case 'category':
|
||||
$assetmodels->OrderCategory($order);
|
||||
break;
|
||||
default:
|
||||
$assetmodels->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$total = $assetmodels->count();
|
||||
$assetmodels = $assetmodels->skip($offset)->take($limit)->get();
|
||||
return (new AssetModelsTransformer)->transformAssetModels($assetmodels, $total);
|
||||
@@ -239,17 +253,17 @@ class AssetModelsController extends Controller
|
||||
$assetmodel->use_text = '';
|
||||
|
||||
if ($settings->modellistCheckedValue('category')) {
|
||||
$assetmodel->use_text .= (($assetmodel->category) ? e($assetmodel->category->name).' - ' : '');
|
||||
$assetmodel->use_text .= (($assetmodel->category) ? $assetmodel->category->name.' - ' : '');
|
||||
}
|
||||
|
||||
if ($settings->modellistCheckedValue('manufacturer')) {
|
||||
$assetmodel->use_text .= (($assetmodel->manufacturer) ? e($assetmodel->manufacturer->name).' ' : '');
|
||||
$assetmodel->use_text .= (($assetmodel->manufacturer) ? $assetmodel->manufacturer->name.' ' : '');
|
||||
}
|
||||
|
||||
$assetmodel->use_text .= e($assetmodel->name);
|
||||
$assetmodel->use_text .= $assetmodel->name;
|
||||
|
||||
if (($settings->modellistCheckedValue('model_number')) && ($assetmodel->model_number!='')) {
|
||||
$assetmodel->use_text .= ' (#'.e($assetmodel->model_number).')';
|
||||
$assetmodel->use_text .= ' (#'.$assetmodel->model_number.')';
|
||||
}
|
||||
|
||||
$assetmodel->use_image = ($settings->modellistCheckedValue('image') && ($assetmodel->image)) ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null;
|
||||
|
||||
@@ -141,8 +141,6 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
|
||||
$offset = (($assets) && (request('offset') > $assets->count())) ? 0 : request('offset', 0);
|
||||
|
||||
|
||||
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
|
||||
// case we override with the actual count, so we should return 0 items.
|
||||
@@ -812,7 +810,7 @@ class AssetsController extends Controller
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
$asset->last_audit_date = date('Y-m-d h:i:s');
|
||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||
|
||||
if ($asset->save()) {
|
||||
$log = $asset->logAudit(request('note'),request('location_id'));
|
||||
|
||||
@@ -20,7 +20,7 @@ class DepreciationsController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = ['id','name','created_at'];
|
||||
$allowed_columns = ['id','name','months','created_at'];
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','user_id','created_at','updated_at');
|
||||
|
||||
|
||||
@@ -103,11 +103,33 @@ class LicensesController extends Controller
|
||||
case 'category':
|
||||
$licenses = $licenses->leftJoin('categories', 'licenses.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
|
||||
break;
|
||||
case 'depreciation':
|
||||
$licenses = $licenses->leftJoin('depreciations', 'licenses.depreciation_id', '=', 'depreciations.id')->orderBy('depreciations.name', $order);
|
||||
break;
|
||||
case 'company':
|
||||
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns = ['id','name','purchase_cost','expiration_date','purchase_order','order_number','notes','purchase_date','serial','company','category','license_name','license_email','free_seats_count','seats'];
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'purchase_cost',
|
||||
'expiration_date',
|
||||
'purchase_order',
|
||||
'order_number',
|
||||
'notes',
|
||||
'purchase_date',
|
||||
'serial',
|
||||
'company',
|
||||
'category',
|
||||
'license_name',
|
||||
'license_email',
|
||||
'free_seats_count',
|
||||
'seats',
|
||||
'termination_date',
|
||||
'depreciation_id'
|
||||
];
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$licenses = $licenses->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,8 @@ use Illuminate\Support\Facades\Notification;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Models\Ldap; // forward-port of v4 LDAP model for Sync
|
||||
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
@@ -76,10 +78,22 @@ class SettingsController extends Controller
|
||||
|
||||
Log::info('Preparing to get sample user set from LDAP directory');
|
||||
// Get a sample of 10 users so user can verify the data is correct
|
||||
$settings = Setting::getSettings();
|
||||
try {
|
||||
Log::info('Testing LDAP sync');
|
||||
error_reporting(E_ALL & ~E_DEPRECATED); // workaround for php7.4, which deprecates ldap_control_paged_result
|
||||
$users = $ldap->testUserImportSync();
|
||||
// $users = $ldap->testUserImportSync(); // from AdLdap2 from v5, disabling and falling back to v4's sync code
|
||||
$users = collect(Ldap::findLdapUsers())->slice(0, 11)->filter(function ($value, $key) { //choosing ELEVEN because one is going to be the count, which we're about to filter out in the next line
|
||||
return is_int($key);
|
||||
})->map(function ($item) use ($settings) {
|
||||
return (object) [
|
||||
'username' => $item[$settings['ldap_username_field']][0] ?? null,
|
||||
'employee_number' => $item[$settings['ldap_emp_num']][0] ?? null,
|
||||
'lastname' => $item[$settings['ldap_lname_field']][0] ?? null,
|
||||
'firstname' => $item[$settings['ldap_fname_field']][0] ?? null,
|
||||
'email' => $item[$settings['ldap_email']][0] ?? null,
|
||||
];
|
||||
});
|
||||
$message['user_sync'] = [
|
||||
'users' => $users
|
||||
];
|
||||
|
||||
@@ -60,6 +60,7 @@ class UsersController extends Controller
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
'users.ldap_import',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department','assets','licenses','accessories','consumables')
|
||||
->withCount('assets as assets_count','licenses as licenses_count','accessories as accessories_count','consumables as consumables_count');
|
||||
@@ -131,7 +132,7 @@ class UsersController extends Controller
|
||||
'assets','accessories', 'consumables','licenses','groups','activated','created_at',
|
||||
'two_factor_enrolled','two_factor_optin','last_login', 'assets_count', 'licenses_count',
|
||||
'consumables_count', 'accessories_count', 'phone', 'address', 'city', 'state',
|
||||
'country', 'zip', 'id'
|
||||
'country', 'zip', 'id', 'ldap_import'
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
@@ -184,16 +185,16 @@ class UsersController extends Controller
|
||||
foreach ($users as $user) {
|
||||
$name_str = '';
|
||||
if ($user->last_name!='') {
|
||||
$name_str .= e($user->last_name).', ';
|
||||
$name_str .= $user->last_name.', ';
|
||||
}
|
||||
$name_str .= e($user->first_name);
|
||||
$name_str .= $user->first_name;
|
||||
|
||||
if ($user->username!='') {
|
||||
$name_str .= ' ('.e($user->username).')';
|
||||
$name_str .= ' ('.$user->username.')';
|
||||
}
|
||||
|
||||
if ($user->employee_num!='') {
|
||||
$name_str .= ' - #'.e($user->employee_num);
|
||||
$name_str .= ' - #'.$user->employee_num;
|
||||
}
|
||||
|
||||
$user->use_text = $name_str;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Assets;
|
||||
|
||||
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AssetCheckoutRequest;
|
||||
@@ -33,7 +34,8 @@ class AssetCheckoutController extends Controller
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
if ($asset->availableForCheckout()) {
|
||||
return view('hardware/checkout', compact('asset'));
|
||||
return view('hardware/checkout', compact('asset'))
|
||||
->with('statusLabel_list', Helper::deployableStatusLabelList());
|
||||
}
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkout.not_available'));
|
||||
|
||||
@@ -75,6 +77,10 @@ class AssetCheckoutController extends Controller
|
||||
$expected_checkin = $request->get('expected_checkin');
|
||||
}
|
||||
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->get('status_id');
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
|
||||
return redirect()->route("hardware.index")->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
@@ -165,10 +165,17 @@ class AssetsController extends Controller
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted=='1') {
|
||||
if (Gate::allows('admin')) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
|
||||
}
|
||||
if(is_array($request->input($field->convertUnicodeDbSlug()))){
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e(implode(', ', $request->input($field->convertUnicodeDbSlug()))));
|
||||
}else{
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
|
||||
} }
|
||||
} else {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
if(is_array($request->input($field->convertUnicodeDbSlug()))){
|
||||
$asset->{$field->convertUnicodeDbSlug()} = implode(', ', $request->input($field->convertUnicodeDbSlug()));
|
||||
}else{
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,10 +349,18 @@ class AssetsController extends Controller
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
if ($field->field_encrypted=='1') {
|
||||
if (Gate::allows('admin')) {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
|
||||
if(is_array($request->input($field->convertUnicodeDbSlug()))){
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e(implode(', ', $request->input($field->convertUnicodeDbSlug()))));
|
||||
}else{
|
||||
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
if(is_array($request->input($field->convertUnicodeDbSlug()))){
|
||||
$asset->{$field->convertUnicodeDbSlug()} = implode(', ', $request->input($field->convertUnicodeDbSlug()));
|
||||
}else{
|
||||
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,13 +105,13 @@ class LoginController extends Controller
|
||||
$samlData = $request->session()->get('saml_login');
|
||||
if ($saml->isEnabled() && !empty($samlData)) {
|
||||
try {
|
||||
LOG::debug("Attempting to log user in by SAML authentication.");
|
||||
Log::debug("Attempting to log user in by SAML authentication.");
|
||||
$user = $saml->samlLogin($samlData);
|
||||
if(!is_null($user)) {
|
||||
Auth::login($user, true);
|
||||
} else {
|
||||
$username = $saml->getUsername();
|
||||
LOG::debug("SAML user '$username' could not be found in database.");
|
||||
Log::error("SAML user '$username' could not be found in database.");
|
||||
$request->session()->flash('error', trans('auth/message.signin.error'));
|
||||
$saml->clearData();
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class LoginController extends Controller
|
||||
$user->save();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
LOG::debug("There was an error authenticating the SAML user: " . $e->getMessage());
|
||||
Log::error("There was an error authenticating the SAML user: " . $e->getMessage());
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -157,9 +157,26 @@ class LoginController extends Controller
|
||||
if (Setting::getSettings()->login_remote_user_enabled == "1" && isset($remote_user) && !empty($remote_user)) {
|
||||
Log::debug("Authenticating via HTTP header $header_name.");
|
||||
|
||||
$pos = strpos($remote_user, '\\');
|
||||
$strip_prefixes = [
|
||||
// IIS/AD
|
||||
// https://github.com/snipe/snipe-it/pull/5862
|
||||
'\\',
|
||||
|
||||
// Google Cloud IAP
|
||||
// https://cloud.google.com/iap/docs/identity-howto#getting_the_users_identity_with_signed_headers
|
||||
'accounts.google.com:',
|
||||
];
|
||||
|
||||
$pos = 0;
|
||||
foreach ($strip_prefixes as $needle) {
|
||||
if (($pos = strpos($remote_user, $needle)) !== FALSE) {
|
||||
$pos += strlen($needle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($pos > 0) {
|
||||
$remote_user = substr($remote_user, $pos + 1);
|
||||
$remote_user = substr($remote_user, $pos);
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -189,8 +206,8 @@ class LoginController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
|
||||
$this->maxLoginAttempts = config('auth.throttle.max_attempts');
|
||||
$this->lockoutTime = config('auth.throttle.lockout_duration');
|
||||
$this->maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
|
||||
$this->lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
|
||||
|
||||
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
@@ -452,8 +469,8 @@ class LoginController extends Controller
|
||||
*/
|
||||
protected function hasTooManyLoginAttempts(Request $request)
|
||||
{
|
||||
$lockoutTime = config('auth.throttle.lockout_duration');
|
||||
$maxLoginAttempts = config('auth.throttle.max_attempts');
|
||||
$lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
|
||||
$maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
|
||||
|
||||
return $this->limiter()->tooManyAttempts(
|
||||
$this->throttleKey($request),
|
||||
|
||||
@@ -101,8 +101,8 @@ class SamlController extends Controller
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (!empty($errors)) {
|
||||
Log::debug("There was an error with SAML ACS: " . implode(', ', $errors));
|
||||
Log::debug("Reason: " . $auth->getLastErrorReason());
|
||||
Log::error("There was an error with SAML ACS: " . implode(', ', $errors));
|
||||
Log::error("Reason: " . $auth->getLastErrorReason());
|
||||
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class SamlController extends Controller
|
||||
* Receives LogoutRequest/LogoutResponse from IdP and flashes
|
||||
* back to the LoginController for logging out.
|
||||
*
|
||||
* /saml/slo
|
||||
* /saml/sls
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
@@ -128,12 +128,13 @@ class SamlController extends Controller
|
||||
public function sls(Request $request)
|
||||
{
|
||||
$auth = $this->saml->getAuth();
|
||||
$sloUrl = $auth->processSLO(true, null, null, null, true);
|
||||
$retrieveParametersFromServer = $this->saml->getSetting('retrieveParametersFromServer', false);
|
||||
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (!empty($errors)) {
|
||||
Log::debug("There was an error with SAML SLS: " . implode(', ', $errors));
|
||||
Log::debug("Reason: " . $auth->getLastErrorReason());
|
||||
Log::error("There was an error with SAML SLS: " . implode(', ', $errors));
|
||||
Log::error("Reason: " . $auth->getLastErrorReason());
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
|
||||
/**
|
||||
* This controller provide the healthz route for
|
||||
* the Snipe-IT Asset Management application.
|
||||
*
|
||||
* @version v1.0
|
||||
*/
|
||||
class HealthController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Returns a fixed JSON content ({ "status": "ok"}) which indicate the app is up and running
|
||||
*/
|
||||
public function get() {
|
||||
return response()->json([
|
||||
"status" => "ok"
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,6 @@ class LocationsController extends Controller
|
||||
{
|
||||
$this->authorize('create', Location::class);
|
||||
$location = new Location();
|
||||
$location->id = null; // This is required to make Laravels different validation work, it errors if the parameter doesn't exist (maybe a bug)?
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
$location->currency = $request->input('currency', '$');
|
||||
@@ -132,7 +131,6 @@ class LocationsController extends Controller
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
// Update the location data
|
||||
$location->name = $request->input('name');
|
||||
$location->parent_id = $request->input('parent_id', null);
|
||||
|
||||
@@ -158,7 +158,7 @@ class ManufacturersController extends Controller
|
||||
public function destroy($manufacturerId)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
if (is_null($manufacturer = Manufacturer::withCount('models as models_count')->find($manufacturerId))) {
|
||||
if (is_null($manufacturer = Manufacturer::withTrashed()->withCount('models as models_count')->find($manufacturerId))) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
|
||||
}
|
||||
|
||||
@@ -174,8 +174,12 @@ class ManufacturersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the manufacturer
|
||||
$manufacturer->delete();
|
||||
// Soft delete the manufacturer if active, permanent delete if is already deleted
|
||||
if($manufacturer->deleted_at === NULL) {
|
||||
$manufacturer->delete();
|
||||
} else {
|
||||
$manufacturer->forceDelete();
|
||||
}
|
||||
// Redirect to the manufacturers management page
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
|
||||
}
|
||||
|
||||
@@ -632,6 +632,14 @@ class ReportsController extends Controller
|
||||
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('last_audit_start')) && ($request->filled('last_audit_end'))) {
|
||||
$assets->whereBetween('assets.last_audit_date', [$request->input('last_audit_start'), $request->input('last_audit_end')]);
|
||||
}
|
||||
|
||||
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
|
||||
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
|
||||
}
|
||||
|
||||
$assets->orderBy('assets.created_at', 'ASC')->chunk(20, function($assets) use($handle, $customfields, $request) {
|
||||
|
||||
|
||||
@@ -577,6 +577,7 @@ class SettingsController extends Controller
|
||||
$setting->default_currency = $request->input('default_currency', '$');
|
||||
$setting->date_display_format = $request->input('date_display_format');
|
||||
$setting->time_display_format = $request->input('time_display_format');
|
||||
$setting->digit_separator = $request->input('digit_separator');
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
@@ -1015,7 +1016,7 @@ class SettingsController extends Controller
|
||||
|
||||
$path = 'app/backups';
|
||||
$backup_files = Storage::files($path);
|
||||
$files = [];
|
||||
$files_raw = [];
|
||||
|
||||
if (count($backup_files) > 0) {
|
||||
for ($f = 0; $f < count($backup_files); ++$f) {
|
||||
@@ -1023,7 +1024,7 @@ class SettingsController extends Controller
|
||||
// Skip dotfiles like .gitignore and .DS_STORE
|
||||
if ((substr(basename($backup_files[$f]), 0, 1) != '.')) {
|
||||
|
||||
$files[] = [
|
||||
$files_raw[] = [
|
||||
'filename' => basename($backup_files[$f]),
|
||||
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
|
||||
'modified' => Storage::lastModified($backup_files[$f]),
|
||||
@@ -1035,6 +1036,9 @@ class SettingsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the array so it lists oldest first
|
||||
$files = array_reverse($files_raw);
|
||||
|
||||
return view('settings/backups', compact('path', 'files'));
|
||||
}
|
||||
|
||||
@@ -1138,6 +1142,7 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function getPurge()
|
||||
{
|
||||
\Log::warning('User ID '.Auth::user()->id.' is attempting a PURGE');
|
||||
return view('settings.purge-form');
|
||||
}
|
||||
|
||||
@@ -1154,6 +1159,8 @@ class SettingsController extends Controller
|
||||
{
|
||||
if (! config('app.lock_passwords')) {
|
||||
if ('DELETE' == $request->input('confirm_purge')) {
|
||||
|
||||
\Log::warning('User ID '.Auth::user()->id.' initiated a PURGE!');
|
||||
// Run a backup immediately before processing
|
||||
Artisan::call('backup:run');
|
||||
Artisan::call('snipeit:purge', ['--force' => 'true', '--no-interaction' => true]);
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Services\LdapAd;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use App\Models\User; // Note that this is awful close to 'Users' the namespace above; be careful
|
||||
|
||||
class LDAPImportController extends Controller
|
||||
{
|
||||
@@ -65,6 +66,7 @@ class LDAPImportController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('update', User::class);
|
||||
// Call Artisan LDAP import command.
|
||||
$location_id = $request->input('location_id');
|
||||
Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]);
|
||||
|
||||
@@ -38,7 +38,7 @@ class UserFilesController extends Controller
|
||||
$filename = 'user-' . $user->id . '-' . str_random(8);
|
||||
$filename .= '-' . str_slug($file->getClientOriginalName()) . '.' . $extension;
|
||||
if (!$file->move($destinationPath, $filename)) {
|
||||
return JsonResponse::create(["error" => "Unabled to move file"], 500);
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.invalidfiles'));
|
||||
}
|
||||
//Log the uploaded file to the log
|
||||
$logAction = new Actionlog();
|
||||
@@ -57,10 +57,10 @@ class UserFilesController extends Controller
|
||||
}
|
||||
$logActions[] = $logAction;
|
||||
}
|
||||
// dd($logActions);
|
||||
return JsonResponse::create($logActions);
|
||||
// dd($logActions);
|
||||
return redirect()->back()->with('success', trans('admin/users/message.upload.success'));
|
||||
}
|
||||
return JsonResponse::create(["error" => "No User associated with this request"], 500);
|
||||
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class Kernel extends HttpKernel
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\NoSessionStore::class,
|
||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
|
||||
@@ -27,7 +27,7 @@ class CheckForSetup
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!($request->is('setup*')) && !($request->is('.env'))) {
|
||||
if (!($request->is('setup*')) && !($request->is('.env')) && !($request->is('health'))) {
|
||||
return redirect(url('/').'/setup');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class NoSessionStore
|
||||
{
|
||||
protected $except = [
|
||||
'health'
|
||||
];
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
foreach ($this->except as $except) {
|
||||
if ($request->is($except)) {
|
||||
config()->set('session.driver', 'array');
|
||||
}
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -12,5 +12,6 @@ class VerifyCsrfToken extends BaseVerifier
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'health'
|
||||
];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class ImageUploadRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
|
||||
'image' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml,webp',
|
||||
'avatar' => 'mimes:png,gif,jpg,jpeg,svg,bmp,svg+xml',
|
||||
];
|
||||
}
|
||||
@@ -91,8 +91,8 @@ class ImageUploadRequest extends Request
|
||||
\Log::info('File name will be: '.$file_name);
|
||||
\Log::debug('File extension is: '. $ext);
|
||||
|
||||
if ($image->getClientOriginalExtension()!=='svg') {
|
||||
\Log::debug('Not an SVG - resize');
|
||||
if (($image->getClientOriginalExtension()!=='webp') && ($image->getClientOriginalExtension()!=='svg')) {
|
||||
\Log::debug('Not an SVG or webp - resize');
|
||||
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
|
||||
$upload = Image::make($image->getRealPath())->resize(null, $w, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
@@ -102,20 +102,27 @@ class ImageUploadRequest extends Request
|
||||
// This requires a string instead of an object, so we use ($string)
|
||||
Storage::disk('public')->put($path.'/'.$file_name, (string)$upload->encode());
|
||||
|
||||
|
||||
// If the file is an SVG, we need to clean it and NOT encode it
|
||||
} else {
|
||||
\Log::debug('This is an SVG');
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($image->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
// If the file is a webp, we need to just move it since webp support
|
||||
// needs to be compiled into gd for resizing to be available
|
||||
if ($image->getClientOriginalExtension()=='webp') {
|
||||
\Log::debug('This is a webp, just move it');
|
||||
Storage::disk('public')->put($path.'/'.$file_name, file_get_contents($image));
|
||||
// If the file is an SVG, we need to clean it and NOT encode it
|
||||
} else {
|
||||
|
||||
try {
|
||||
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
|
||||
Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Upload no workie :( ');
|
||||
\Log::debug($e);
|
||||
\Log::debug('This is an SVG');
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($image->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
\Log::debug('Trying to upload to: '.$path.'/'.$file_name);
|
||||
Storage::disk('public')->put($path.'/'.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug('Upload no workie :( ');
|
||||
\Log::debug($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,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|confirmed',
|
||||
'password' => 'required|min:8|confirmed',
|
||||
'email_domain' => 'required|min:4',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ class LicensesTransformer
|
||||
'order_number' => e($license->order_number),
|
||||
'purchase_order' => e($license->purchase_order),
|
||||
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
|
||||
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
|
||||
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
||||
'purchase_cost' => e($license->purchase_cost),
|
||||
'notes' => e($license->notes),
|
||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||
|
||||
@@ -25,15 +25,15 @@ class SelectlistTransformer
|
||||
foreach ($select_items as $select_item) {
|
||||
$items_array[]= [
|
||||
'id' => (int) $select_item->id,
|
||||
'text' => ($select_item->use_text) ? e($select_item->use_text) : e($select_item->name),
|
||||
'image' => ($select_item->use_image) ? e($select_item->use_image) : null,
|
||||
'text' => ($select_item->use_text) ? $select_item->use_text : $select_item->name,
|
||||
'image' => ($select_item->use_image) ? $select_item->use_image : null,
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
$results = [
|
||||
'items' => $items_array,
|
||||
'results' => $items_array,
|
||||
'pagination' =>
|
||||
[
|
||||
'more' => ($select_items->currentPage() >= $select_items->lastPage()) ? false : true,
|
||||
|
||||
@@ -52,6 +52,7 @@ class UsersTransformer
|
||||
'notes'=> e($user->notes),
|
||||
'permissions' => $user->decodePermissions(),
|
||||
'activated' => ($user->activated =='1') ? true : false,
|
||||
'ldap_import' => ($user->ldap_import =='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,
|
||||
|
||||
@@ -125,7 +125,10 @@ class UserImporter extends ItemImporter
|
||||
if ($department) {
|
||||
$this->log('A matching department ' . $department_name . ' already exists');
|
||||
return $department->id;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$department = new department();
|
||||
$department->name = $department_name;
|
||||
$department->user_id = $this->user_id;
|
||||
@@ -134,7 +137,8 @@ class UserImporter extends ItemImporter
|
||||
$this->log('department ' . $department_name . ' was created');
|
||||
return $department->id;
|
||||
}
|
||||
$this->logError($department, 'Company');
|
||||
|
||||
$this->logError($department, 'Department');
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+13
-7
@@ -166,6 +166,7 @@ class Asset extends Depreciable
|
||||
'supplier' => ['name'],
|
||||
'company' => ['name'],
|
||||
'defaultLoc' => ['name'],
|
||||
'location' => ['name'],
|
||||
'model' => ['name', 'model_number'],
|
||||
'model.category' => ['name'],
|
||||
'model.manufacturer' => ['name'],
|
||||
@@ -245,13 +246,18 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function availableForCheckout()
|
||||
{
|
||||
if (
|
||||
((!$this->assignedTo) && ($this->assetstatus->archived == 0)) ||
|
||||
((empty($this->assigned_to)) &&
|
||||
(empty($this->deleted_at)) &&
|
||||
(($this->assetstatus) && ($this->assetstatus->deployable == 1))))
|
||||
{
|
||||
return true;
|
||||
|
||||
// This asset is not currently assigned to anyone and is not deleted...
|
||||
if ((!$this->assigned_to) && (!$this->deleted_at)) {
|
||||
|
||||
// The asset status is not archived and is deployable
|
||||
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
|
||||
&& ($this->assetstatus->deployable == '1'))
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,9 +285,13 @@ class CustomField extends Model
|
||||
*/
|
||||
public function formatFieldValuesAsArray()
|
||||
{
|
||||
$result = [];
|
||||
$arr = preg_split("/\\r\\n|\\r|\\n/", $this->field_values);
|
||||
|
||||
$result[''] = 'Select '.strtolower($this->format);
|
||||
if (($this->element!='checkbox') && ($this->element!='radio')) {
|
||||
$result[''] = 'Select '.strtolower($this->format);
|
||||
}
|
||||
|
||||
|
||||
for ($x = 0; $x < count($arr); $x++) {
|
||||
$arr_parts = explode('|', $arr[$x]);
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\User;
|
||||
use App\Models\Setting;
|
||||
use Exception;
|
||||
use Input;
|
||||
use Log;
|
||||
|
||||
class Ldap extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* Makes a connection to LDAP using the settings in Admin > Settings.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return connection
|
||||
*/
|
||||
|
||||
public static function connectToLdap()
|
||||
{
|
||||
|
||||
$ldap_host = Setting::getSettings()->ldap_server;
|
||||
$ldap_version = Setting::getSettings()->ldap_version;
|
||||
$ldap_server_cert_ignore = Setting::getSettings()->ldap_server_cert_ignore;
|
||||
$ldap_use_tls = Setting::getSettings()->ldap_tls;
|
||||
|
||||
|
||||
// If we are ignoring the SSL cert we need to setup the environment variable
|
||||
// before we create the connection
|
||||
if ($ldap_server_cert_ignore=='1') {
|
||||
putenv('LDAPTLS_REQCERT=never');
|
||||
}
|
||||
|
||||
// If the user specifies where CA Certs are, make sure to use them
|
||||
if (env("LDAPTLS_CACERT")) {
|
||||
putenv("LDAPTLS_CACERT=".env("LDAPTLS_CACERT"));
|
||||
}
|
||||
|
||||
$connection = @ldap_connect($ldap_host);
|
||||
|
||||
if (!$connection) {
|
||||
throw new Exception('Could not connect to LDAP server at '.$ldap_host.'. Please check your LDAP server name and port number in your settings.');
|
||||
}
|
||||
|
||||
// Needed for AD
|
||||
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);
|
||||
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
|
||||
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
|
||||
|
||||
if ($ldap_use_tls=='1') {
|
||||
ldap_start_tls($connection);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds/authenticates the user to LDAP, and returns their attributes.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param bool|false $user
|
||||
* @return bool true if the username and/or password provided are valid
|
||||
* false if the username and/or password provided are invalid
|
||||
* array of ldap_attributes if $user is true
|
||||
*
|
||||
*/
|
||||
static function findAndBindUserLdap($username, $password)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$connection = Ldap::connectToLdap();
|
||||
$ldap_username_field = $settings->ldap_username_field;
|
||||
$baseDn = $settings->ldap_basedn;
|
||||
$userDn = $ldap_username_field.'='.$username.','.$settings->ldap_basedn;
|
||||
|
||||
if ($settings->is_ad =='1') {
|
||||
// Check if they are using the userprincipalname for the username field.
|
||||
// If they are, we can skip building the UPN to authenticate against AD
|
||||
if ($ldap_username_field=='userprincipalname') {
|
||||
$userDn = $username;
|
||||
} else {
|
||||
// In case they haven't added an AD domain
|
||||
$userDn = ($settings->ad_domain != '') ? $username.'@'.$settings->ad_domain : $username.'@'.$settings->email_domain;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
\Log::debug('Attempting to login using distinguished name:'.$userDn);
|
||||
|
||||
|
||||
$filterQuery = $settings->ldap_auth_filter_query . $username;
|
||||
|
||||
|
||||
if (!$ldapbind = @ldap_bind($connection, $userDn, $password)) {
|
||||
if(!$ldapbind = Ldap::bindAdminToLdap($connection)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$results = ldap_search($connection, $baseDn, $filterQuery)) {
|
||||
throw new Exception('Could not search LDAP: ');
|
||||
}
|
||||
|
||||
if (!$entry = ldap_first_entry($connection, $results)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$user = ldap_get_attributes($connection, $entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_change_key_case($user);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds/authenticates an admin to LDAP for LDAP searching/syncing.
|
||||
* Here we also return a better error if the app key is donked.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param bool|false $user
|
||||
* @return bool true if the username and/or password provided are valid
|
||||
* false if the username and/or password provided are invalid
|
||||
*
|
||||
*/
|
||||
static function bindAdminToLdap($connection)
|
||||
{
|
||||
|
||||
$ldap_username = Setting::getSettings()->ldap_uname;
|
||||
|
||||
// Lets return some nicer messages for users who donked their app key, and disable LDAP
|
||||
try {
|
||||
$ldap_pass = \Crypt::decrypt(Setting::getSettings()->ldap_pword);
|
||||
} catch (Exception $e) {
|
||||
|
||||
throw new Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
|
||||
}
|
||||
|
||||
|
||||
if (!$ldapbind = @ldap_bind($connection, $ldap_username, $ldap_pass)) {
|
||||
throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse and map LDAP attributes based on settings
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @param $ldapatttibutes
|
||||
* @return array|bool
|
||||
*/
|
||||
static function parseAndMapLdapAttributes($ldapatttibutes)
|
||||
{
|
||||
//Get LDAP attribute config
|
||||
$ldap_result_username = Setting::getSettings()->ldap_username_field;
|
||||
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
|
||||
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
|
||||
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
|
||||
$ldap_result_email = Setting::getSettings()->ldap_email;
|
||||
|
||||
// Get LDAP user data
|
||||
$item = array();
|
||||
$item["username"] = isset($ldapatttibutes[$ldap_result_username][0]) ? $ldapatttibutes[$ldap_result_username][0] : "";
|
||||
$item["employee_number"] = isset($ldapatttibutes[$ldap_result_emp_num][0]) ? $ldapatttibutes[$ldap_result_emp_num][0] : "";
|
||||
$item["lastname"] = isset($ldapatttibutes[$ldap_result_last_name][0]) ? $ldapatttibutes[$ldap_result_last_name][0] : "";
|
||||
$item["firstname"] = isset($ldapatttibutes[$ldap_result_first_name][0]) ? $ldapatttibutes[$ldap_result_first_name][0] : "";
|
||||
$item["email"] = isset($ldapatttibutes[$ldap_result_email][0]) ? $ldapatttibutes[$ldap_result_email][0] : "" ;
|
||||
|
||||
return $item;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create user from LDAP attributes
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $ldapatttibutes
|
||||
* @return array|bool
|
||||
*/
|
||||
static function createUserFromLdap($ldapatttibutes)
|
||||
{
|
||||
$item = Ldap::parseAndMapLdapAttributes($ldapatttibutes);
|
||||
|
||||
|
||||
// Create user from LDAP data
|
||||
if (!empty($item["username"])) {
|
||||
$user = new User;
|
||||
$user->first_name = $item["firstname"];
|
||||
$user->last_name = $item["lastname"];
|
||||
$user->username = $item["username"];
|
||||
$user->email = $item["email"];
|
||||
|
||||
if (Setting::getSettings()->ldap_pw_sync=='1') {
|
||||
$user->password = bcrypt(Input::get("password"));
|
||||
} else {
|
||||
$pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 25);
|
||||
$user->password = bcrypt($pass);
|
||||
}
|
||||
|
||||
$user->activated = 1;
|
||||
$user->ldap_import = 1;
|
||||
$user->notes = 'Imported on first login from LDAP';
|
||||
|
||||
if ($user->save()) {
|
||||
return $user;
|
||||
} else {
|
||||
LOG::debug('Could not create user.'.$user->getErrors());
|
||||
throw new Exception("Could not create user: ".$user->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches LDAP
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $ldapatttibutes
|
||||
* @param $base_dn
|
||||
* @return array|bool
|
||||
*/
|
||||
static function findLdapUsers($base_dn = null)
|
||||
{
|
||||
|
||||
$ldapconn = Ldap::connectToLdap();
|
||||
$ldap_bind = Ldap::bindAdminToLdap($ldapconn);
|
||||
// Default to global base DN if nothing else is provided.
|
||||
if (is_null($base_dn)) {
|
||||
$base_dn = Setting::getSettings()->ldap_basedn;
|
||||
}
|
||||
$filter = Setting::getSettings()->ldap_filter;
|
||||
|
||||
// Set up LDAP pagination for very large databases
|
||||
$page_size = 500;
|
||||
$cookie = '';
|
||||
$result_set = array();
|
||||
$global_count = 0;
|
||||
|
||||
// Perform the search
|
||||
do {
|
||||
|
||||
// Paginate (non-critical, if not supported by server)
|
||||
if (!$ldap_paging = @ldap_control_paged_result($ldapconn, $page_size, false, $cookie)) {
|
||||
throw new Exception('Problem with your LDAP connection. Try checking the Use TLS setting in Admin > Settings. ');
|
||||
}
|
||||
|
||||
if ($filter != '' && substr($filter, 0, 1) != '(') { // wrap parens around NON-EMPTY filters that DON'T have them, for back-compatibility with AdLdap2-based filters
|
||||
$filter = "($filter)";
|
||||
}
|
||||
$search_results = ldap_search($ldapconn, $base_dn, $filter);
|
||||
|
||||
if (!$search_results) {
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
|
||||
}
|
||||
|
||||
// Get results from page
|
||||
$results = ldap_get_entries($ldapconn, $search_results);
|
||||
if (!$results) {
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn)); // FIXME this is never called in any routed context - only from the Artisan command. So this redirect will never work.
|
||||
}
|
||||
|
||||
// Add results to result set
|
||||
$global_count += $results['count'];
|
||||
$result_set = array_merge($result_set, $results);
|
||||
|
||||
@ldap_control_paged_result_response($ldapconn, $search_results, $cookie);
|
||||
|
||||
} while ($cookie !== null && $cookie != '');
|
||||
|
||||
|
||||
// Clean up after search
|
||||
$result_set['count'] = $global_count;
|
||||
$results = $result_set;
|
||||
@ldap_control_paged_result($ldapconn, 0);
|
||||
|
||||
return $results;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,7 @@ class License extends Depreciable
|
||||
'manufacturer' => ['name'],
|
||||
'company' => ['name'],
|
||||
'category' => ['name'],
|
||||
'depreciation' => ['name'],
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ class Location extends SnipeModel
|
||||
'address2' => 'max:80|nullable',
|
||||
'zip' => 'min:3|max:10|nullable',
|
||||
'manager_id' => 'exists:users,id|nullable',
|
||||
'parent_id' => 'nullable|exists:locations,id|different:id',
|
||||
'parent_id' => 'non_circular:locations,id'
|
||||
);
|
||||
|
||||
protected $casts = [
|
||||
|
||||
@@ -17,7 +17,7 @@ class Manufacturer extends SnipeModel
|
||||
|
||||
// Declare the rules for the form validation
|
||||
protected $rules = array(
|
||||
'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,deleted_at',
|
||||
'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL',
|
||||
'url' => 'url|nullable',
|
||||
'support_url' => 'url|nullable',
|
||||
'support_email' => 'email|nullable'
|
||||
|
||||
@@ -48,7 +48,6 @@ class Setting extends Model
|
||||
protected $rules = [
|
||||
'brand' => 'required|min:1|numeric',
|
||||
'qr_text' => 'max:31|nullable',
|
||||
'logo_img' => 'mimes:jpeg,bmp,png,gif',
|
||||
'alert_email' => 'email_array|nullable',
|
||||
'admin_cc_email' => 'email|nullable',
|
||||
'default_currency' => 'required',
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
class PredefinedKitPolicy extends SnipePermissionsPolicy
|
||||
{
|
||||
protected function columnName()
|
||||
{
|
||||
return 'kits';
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,129 @@ namespace App\Presenters;
|
||||
*/
|
||||
class AssetModelPresenter extends Presenter
|
||||
{
|
||||
|
||||
public static function dataTableLayout() {
|
||||
|
||||
$layout = [
|
||||
[
|
||||
"field" => "checkbox",
|
||||
"checkbox" => true
|
||||
],
|
||||
[
|
||||
"field" => "id",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.id'),
|
||||
"visible" => false
|
||||
], [
|
||||
"field" => "company",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/companies/table.title'),
|
||||
"visible" => false,
|
||||
"formatter" => "companiesLinkObjFormatter"
|
||||
], [
|
||||
"field" => "name",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => true,
|
||||
"title" => trans('general.name'),
|
||||
"formatter" => "modelsLinkFormatter"
|
||||
],
|
||||
[
|
||||
"field" => "image",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.image'),
|
||||
"visible" => true,
|
||||
"formatter" => 'imageFormatter',
|
||||
],
|
||||
[
|
||||
"field" => "manufacturer",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.manufacturer'),
|
||||
"visible" => false,
|
||||
"formatter" => 'manufacturersLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
"field" => "model_number",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/models/table.modelnumber'),
|
||||
"visible" => true,
|
||||
],
|
||||
[
|
||||
"field" => "assets_count",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/models/table.numassets'),
|
||||
"visible" => true,
|
||||
],
|
||||
[
|
||||
"field" => "depreciation",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.depreciation'),
|
||||
"visible" => false,
|
||||
"formatter" => "depreciationsLinkObjFormatter",
|
||||
],
|
||||
[
|
||||
"field" => "category",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.category'),
|
||||
"visible" => false,
|
||||
"formatter" => "categoriesLinkObjFormatter",
|
||||
],
|
||||
[
|
||||
"field" => "eol",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.eol'),
|
||||
"visible" => true,
|
||||
],
|
||||
[
|
||||
"field" => "fieldset",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/models/general.fieldset'),
|
||||
"visible" => true,
|
||||
"formatter" => "fieldsetsLinkObjFormatter",
|
||||
],
|
||||
[
|
||||
"field" => "notes",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('general.notes'),
|
||||
"visible" => false,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$layout[] = [
|
||||
"field" => "actions",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"switchable" => false,
|
||||
"title" => trans('table.actions'),
|
||||
"formatter" => "licensesActionsFormatter",
|
||||
];
|
||||
|
||||
|
||||
return json_encode($layout);
|
||||
|
||||
}
|
||||
/**
|
||||
* Formatted note for this model
|
||||
* @return string
|
||||
|
||||
@@ -146,6 +146,7 @@ class AssetPresenter extends Presenter
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('general.purchase_cost'),
|
||||
"formatter" => 'numberWithCommas',
|
||||
"footerFormatter" => 'sumFormatter',
|
||||
], [
|
||||
"field" => "order_number",
|
||||
@@ -360,18 +361,13 @@ class AssetPresenter extends Presenter
|
||||
/**
|
||||
* Get Displayable Name
|
||||
* @return string
|
||||
*
|
||||
* @todo this should be factored out - it should be subsumed by fullName (below)
|
||||
*
|
||||
**/
|
||||
public function name()
|
||||
{
|
||||
|
||||
if (empty($this->model->name)) {
|
||||
if (isset($this->model->model)) {
|
||||
return $this->model->model->name.' ('.$this->model->asset_tag.')';
|
||||
}
|
||||
return $this->model->asset_tag;
|
||||
}
|
||||
return $this->model->name . ' (' . $this->model->asset_tag . ')';
|
||||
|
||||
return $this->fullName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,13 +377,18 @@ class AssetPresenter extends Presenter
|
||||
public function fullName()
|
||||
{
|
||||
$str = '';
|
||||
|
||||
// Asset name
|
||||
if ($this->model->name) {
|
||||
$str .= $this->name;
|
||||
$str .= $this->model->name;
|
||||
}
|
||||
|
||||
// Asset tag
|
||||
if ($this->asset_tag) {
|
||||
$str .= ' ('.$this->model->asset_tag.')';
|
||||
}
|
||||
|
||||
// Asset Model name
|
||||
if ($this->model->model) {
|
||||
$str .= ' - '.$this->model->model->name;
|
||||
}
|
||||
|
||||
@@ -8,5 +8,49 @@ namespace App\Presenters;
|
||||
*/
|
||||
class DepreciationPresenter 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" => "name",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('general.name'),
|
||||
"visible" => true,
|
||||
"formatter" => 'depreciationsLinkFormatter',
|
||||
],
|
||||
|
||||
[
|
||||
"field" => "months",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"title" => trans('admin/depreciations/table.term'),
|
||||
"visible" => true,
|
||||
],
|
||||
|
||||
[
|
||||
"field" => "actions",
|
||||
"searchable" => false,
|
||||
"sortable" => false,
|
||||
"switchable" => false,
|
||||
"title" => trans('table.actions'),
|
||||
"visible" => true,
|
||||
"formatter" => "depreciationsActionsFormatter",
|
||||
]
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -97,7 +97,26 @@ class LicensePresenter extends Presenter
|
||||
"visible" => false,
|
||||
"title" => trans('general.purchase_date'),
|
||||
'formatter' => 'dateDisplayFormatter'
|
||||
], [
|
||||
],
|
||||
[
|
||||
"field" => "termination_date",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"visible" => false,
|
||||
"title" => trans('admin/licenses/form.termination_date'),
|
||||
'formatter' => 'dateDisplayFormatter'
|
||||
],
|
||||
[
|
||||
"field" => "depreciation",
|
||||
"searchable" => true,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/hardware/form.depreciation'),
|
||||
"visible" => false,
|
||||
"formatter" => "depreciationsLinkObjFormatter",
|
||||
],
|
||||
|
||||
[
|
||||
"field" => "maintained",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
|
||||
@@ -225,6 +225,15 @@ class UserPresenter extends Presenter
|
||||
"visible" => true,
|
||||
'formatter' => 'groupsFormatter'
|
||||
],
|
||||
[
|
||||
"field" => "ldap_import",
|
||||
"searchable" => false,
|
||||
"sortable" => true,
|
||||
"switchable" => true,
|
||||
"title" => trans('admin/settings/general.ldap_enabled'),
|
||||
"visible" => false,
|
||||
'formatter' => 'trueFalseFormatter'
|
||||
],
|
||||
[
|
||||
"field" => "two_factor_enrolled",
|
||||
"searchable" => false,
|
||||
@@ -301,7 +310,7 @@ class UserPresenter extends Presenter
|
||||
*/
|
||||
public function fullName()
|
||||
{
|
||||
return "{$this->first_name} {$this->last_name}";
|
||||
return html_entity_decode($this->first_name.' '.$this->last_name, ENT_QUOTES | ENT_XML1, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\Depreciation;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\PredefinedKit;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
@@ -33,6 +34,7 @@ use App\Policies\DepreciationPolicy;
|
||||
use App\Policies\LicensePolicy;
|
||||
use App\Policies\LocationPolicy;
|
||||
use App\Policies\ManufacturerPolicy;
|
||||
use App\Policies\PredefinedKitPolicy;
|
||||
use App\Policies\StatuslabelPolicy;
|
||||
use App\Policies\SupplierPolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
@@ -63,6 +65,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
Depreciation::class => DepreciationPolicy::class,
|
||||
License::class => LicensePolicy::class,
|
||||
Location::class => LocationPolicy::class,
|
||||
PredefinedKit::class => PredefinedKitPolicy::class,
|
||||
Statuslabel::class => StatuslabelPolicy::class,
|
||||
Supplier::class => SupplierPolicy::class,
|
||||
User::class => UserPolicy::class,
|
||||
|
||||
@@ -58,6 +58,52 @@ class ValidationServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
|
||||
// Prevent circular references
|
||||
//
|
||||
// Example usage in Location model where parent_id references another Location:
|
||||
//
|
||||
// protected $rules = array(
|
||||
// 'parent_id' => 'non_circular:locations,id,10'
|
||||
// );
|
||||
//
|
||||
Validator::extend('non_circular', function ($attribute, $value, $parameters, $validator) {
|
||||
if (count($parameters) < 2) {
|
||||
throw new \Exception('Required validator parameters: <table>,<primary key>[,depth]');
|
||||
}
|
||||
|
||||
// Parameters from the rule implementation ($pk will likely be 'id')
|
||||
$table = array_get($parameters, 0);
|
||||
$pk = array_get($parameters, 1);
|
||||
$depth = (int) array_get($parameters, 2, 50);
|
||||
|
||||
// Data from the edited model
|
||||
$data = $validator->getData();
|
||||
|
||||
// The primary key value from the edited model
|
||||
$data_pk = array_get($data, $pk);
|
||||
$value_pk = $value;
|
||||
|
||||
// If we’re editing an existing model and there is a parent value set…
|
||||
while ($data_pk && $value_pk) {
|
||||
|
||||
// It’s not valid for any parent id to be equal to the existing model’s id
|
||||
if ($data_pk == $value_pk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid accidental infinite loops
|
||||
if (--$depth < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the next parent id
|
||||
$value_pk = DB::table($table)->select($attribute)->where($pk, '=', $value_pk)->value($attribute);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
// Yo dawg. I heard you like validators.
|
||||
// This validates the custom validator regex in custom fields.
|
||||
// We're just checking that the regex won't throw an exception, not
|
||||
|
||||
+10
-3
@@ -235,15 +235,20 @@ class LdapAd extends LdapAdConfiguration
|
||||
$user->employee_num = trim($userInfo['employee_number']);
|
||||
$user->jobtitle = trim($userInfo['title']);
|
||||
$user->phone = trim($userInfo['telephonenumber']);
|
||||
if(array_key_exists('activated',$userInfo)) {
|
||||
if (array_key_exists('activated',$userInfo)) {
|
||||
$user->activated = $userInfo['activated'];
|
||||
} else if ( !$user->exists ) { // no 'activated' flag was set or unset, *AND* this user is new - activate by default.
|
||||
$user->activated = 1;
|
||||
}
|
||||
if(array_key_exists('location_id',$userInfo)) {
|
||||
if (array_key_exists('location_id',$userInfo)) {
|
||||
$user->location_id = $userInfo['location_id'];
|
||||
}
|
||||
$user->notes = 'Imported from LDAP';
|
||||
|
||||
// this is a new user
|
||||
if (!isset($user->id)) {
|
||||
$user->notes = 'Imported from LDAP';
|
||||
}
|
||||
|
||||
$user->ldap_import = 1;
|
||||
|
||||
return $user;
|
||||
@@ -332,6 +337,7 @@ class LdapAd extends LdapAdConfiguration
|
||||
$activeStatus = (in_array($user->getUserAccountControl(), self::AD_USER_ACCOUNT_CONTROL_FLAGS)) ? 1 : 0;
|
||||
} else {
|
||||
|
||||
//\Log::debug('This looks like LDAP (or an AD where the UAC is disabled)');
|
||||
// If there is no activated flag, then we can't make any determination about activated/deactivated
|
||||
if (false == $this->ldapSettings['ldap_active_flag']) {
|
||||
\Log::debug('ldap_active_flag is false - no ldap_active_flag is set');
|
||||
@@ -548,6 +554,7 @@ class LdapAd extends LdapAdConfiguration
|
||||
if (!is_null($filter)) {
|
||||
$search = $search->rawFilter($filter);
|
||||
}
|
||||
//I think it might be possible to potentially do our own paging here?
|
||||
|
||||
return $search->select($this->getSelectedFields())
|
||||
->paginate(self::PAGE_SIZE);
|
||||
|
||||
@@ -324,6 +324,20 @@ class Saml
|
||||
return $this->_auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a setting.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @param string|array|int $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getSetting($key, $default = null) {
|
||||
return data_get($this->_settings, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SP metadata. The XML representation.
|
||||
*
|
||||
|
||||
+6
-6
@@ -11,13 +11,14 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"php": "^7.2.5",
|
||||
"ext-curl": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"adldap2/adldap2": "^10.2",
|
||||
"alek13/slack": "^1.12",
|
||||
"bacon/bacon-qr-code": "^1.0",
|
||||
"barryvdh/laravel-cors": "^0.11.3",
|
||||
"barryvdh/laravel-debugbar": "^3.2",
|
||||
@@ -33,16 +34,15 @@
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
"intervention/image": "^2.5",
|
||||
"javiereguiluz/easyslugger": "^1.0",
|
||||
"laravel/framework": "^6.0",
|
||||
"laravel/framework": "^6.20",
|
||||
"laravel/helpers": "^1.2",
|
||||
"laravel/passport": "^8.4",
|
||||
"laravel/passport": "^9.3.2",
|
||||
"laravel/slack-notification-channel": "^2.0",
|
||||
"laravel/tinker": "^2.4",
|
||||
"laravelcollective/html": "^6.0",
|
||||
"league/csv": "^9.5",
|
||||
"league/flysystem-aws-s3-v3": "^1.0",
|
||||
"league/flysystem-cached-adapter": "^1.0",
|
||||
"maknz/slack": "^1.7",
|
||||
"neitanod/forceutf8": "^2.0",
|
||||
"nesbot/carbon": "^2.32",
|
||||
"onelogin/php-saml": "^3.4",
|
||||
@@ -110,7 +110,7 @@
|
||||
"optimize-autoloader": true,
|
||||
"process-timeout": 3000,
|
||||
"platform": {
|
||||
"php": "7.2"
|
||||
"php": "7.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+2891
-1248
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -279,7 +279,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'min_php' => '7.1.3',
|
||||
'min_php' => '7.2.5',
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+4
-1
@@ -103,7 +103,10 @@ return [
|
||||
'email' => 'auth.emails.password',
|
||||
'table' => 'password_resets',
|
||||
'expire' => env('RESET_PASSWORD_LINK_EXPIRES', 900),
|
||||
'throttle' => env('LOGIN_MAX_ATTEMPTS', 60),
|
||||
'throttle' => [
|
||||
'max_attempts' => env('LOGIN_MAX_ATTEMPTS', 5),
|
||||
'lockout_duration' => env('LOGIN_LOCKOUT_DURATION', 60)
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -67,6 +67,12 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
'stdout' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => StreamHandler::class,
|
||||
'with' => [ 'stream' => 'php://stdout', ],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('APP_LOG_LEVEL', 'error'),
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v5.0.9',
|
||||
'full_app_version' => 'v5.0.9 - build 5616-973eacf6c3',
|
||||
'build_version' => '5616',
|
||||
'app_version' => 'v5.1.2',
|
||||
'full_app_version' => 'v5.1.2 - build 5847-g00a7c1e9e',
|
||||
'build_version' => '5847',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => '973eacf6c3',
|
||||
'full_hash' => 'v5.0.9-87-973eacf6c3',
|
||||
'hash_version' => 'g00a7c1e9e',
|
||||
'full_hash' => 'v5.1.2-33-g00a7c1e9e',
|
||||
'branch' => 'master',
|
||||
);
|
||||
@@ -17,7 +17,7 @@ class CreateCheckoutAcceptancesTable extends Migration
|
||||
$table->increments('id');
|
||||
|
||||
$table->morphs('checkoutable');
|
||||
$table->integer('assigned_to_id')->unsigned();
|
||||
$table->integer('assigned_to_id')->nullable();
|
||||
|
||||
$table->string('signature_filename')->nullable();
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddDigitSeparatorToSettings extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->char('digit_separator')->nullable()->default('1234.56');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('digit_separator');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SwapTargetTypeIndexOrder extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->dropIndex(['target_id', 'target_type']);
|
||||
});
|
||||
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->index(['target_type', 'target_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->dropIndex(['target_type', 'target_id']);
|
||||
});
|
||||
|
||||
Schema::table('action_logs', function (Blueprint $table) {
|
||||
$table->index(['target_id', 'target_type']);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateMinPassword extends Migration
|
||||
{
|
||||
/**
|
||||
* This migration solves the issue of settings with a minimum password requirement
|
||||
* that is below the actual Snipe-IT minimum requirement in v5 (min 5 became min 8).
|
||||
*
|
||||
* Even though we documented the change in all of the v5 releases, we were still
|
||||
* running into issues where admins did not update their password minimum length
|
||||
* and could not save settings elsewhere, and would not see a warning.
|
||||
*
|
||||
* @todo Loosen up the model level validation for the Settings model and rely on
|
||||
* FormRequests where it makes more sense. Having a form that returns no useful
|
||||
* errors is a bad design pattern.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
App\Models\Setting::where('pwd_secure_min', '<', '8')
|
||||
->update(['pwd_secure_min' => '8']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class FixBadLdapServerUrlForV5 extends Migration
|
||||
{
|
||||
/**
|
||||
* Under v4 and previous versions of Snipe-IT, we permitted users to incorrectly specify LDAP URL's in their settings, and Snipe-IT
|
||||
* would silently permit that.
|
||||
*
|
||||
* v5's LDAP system is not so lenient, and requires either ldap:// or ldaps:// in front of the server's URL. This migration tries
|
||||
* to find misconfigured LDAP URL's and prepend 'ldap://' to them. (That's what we assumed if we *didn't* see ldaps://)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// UPDATE settings SET ldap_server = CONCAT('ldap://',ldap_server) WHERE ldap_server NOT LIKE 'ldap://%' AND ldap_server NOT LIKE 'ldaps://%'
|
||||
$settings = App\Models\Setting::where("ldap_server","not like","ldap://%")->where("ldap_server","not like","ldaps://%");
|
||||
foreach($settings->get() AS $setting) { // we don't formally support having multiple settings records, but just in case they come up...
|
||||
$setting->ldap_server = "ldap://".$setting->ldap_server;
|
||||
$setting->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// Since previous versions supported ldap:// URL's just fine, we don't need to migrate these changes back out on rollback.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddProviderToOauthTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* Sigh. https://github.com/laravel/passport/blob/master/UPGRADE.md#upgrading-to-90-from-8x
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// Add a 'provider' column if not existing or else modify it
|
||||
if (!Schema::hasColumn('oauth_clients', 'provider')) {
|
||||
Schema::table('oauth_clients', function (Blueprint $table) {
|
||||
$table->string('provider')->after('secret')->nullable();
|
||||
});
|
||||
} else {
|
||||
Schema::table('oauth_clients', function (Blueprint $table) {
|
||||
$table->string('provider')->after('secret')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if (Schema::hasColumn('oauth_clients', 'provider')) {
|
||||
Schema::table('oauth_clients', function (Blueprint $table) {
|
||||
$table->dropColumn('provider');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class SettingsSeeder extends Seeder
|
||||
$settings->per_page = 20;
|
||||
$settings->site_name = 'Snipe-IT Demo';
|
||||
$settings->auto_increment_assets = 1;
|
||||
$settings->logo = 'logo.png';
|
||||
$settings->logo = 'snipe-logo.png';
|
||||
$settings->alert_email = 'service@snipe-it.io';
|
||||
$settings->header_color = null;
|
||||
$settings->barcode_type = 'QRCODE';
|
||||
|
||||
@@ -49,5 +49,7 @@ php artisan migrate --force
|
||||
php artisan config:clear
|
||||
php artisan config:cache
|
||||
|
||||
chown -R apache:root /var/www/html/storage/logs/laravel.log
|
||||
|
||||
export APACHE_LOG_DIR=/var/log/apache2
|
||||
exec httpd -DNO_DETACH < /dev/null
|
||||
|
||||
Generated
+840
-482
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@
|
||||
"less-loader": "^4.1.0",
|
||||
"list.js": "^1.5.0",
|
||||
"papaparse": "^4.3.3",
|
||||
"phantomjs": "^2.1.7",
|
||||
"select2": "4.0.13",
|
||||
"tableexport.jquery.plugin": "^1.10.20",
|
||||
"tether": "^1.4.0",
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
.skin-black .main-header .navbar{background-color:#111}.skin-black .main-header .navbar .nav>li>a{color:#fff}.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav>.active>a,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-black .main-header .navbar .sidebar-toggle{color:#fff}.skin-black .main-header .navbar .sidebar-toggle:hover{background-color:#040404}@media (max-width:767px){.skin-black .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-black .main-header .navbar .dropdown-menu li a{color:#fff}.skin-black .main-header .navbar .dropdown-menu li a:hover{background:#040404}}.skin-black .main-header li.user-header{background-color:#111}.skin-black .content-header{background:transparent}.skin-black .left-side,.skin-black .main-sidebar,.skin-black .wrapper{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#111}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .treeview-menu>li>a{color:#8aa4af}.skin-black .treeview-menu>li.active>a,.skin-black .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-black .sidebar-form .btn,.skin-black .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black .sidebar-form input[type=text]:focus,.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn,.btn:hover{text-decoration:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#505156;border-color:#b5bbc8;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#111;border-color:#1f1f21;color:#fff}.btn.btn-white:hover,.btn.btn-white:link,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:link,.btn:hover.btn-white:visited{color:#fff}a{color:#111;text-decoration:underline}a:hover{color:#000}a:visited{color:#111}.text-primary{color:#000}.skin-black .main-header .navbar .nav>li>a{text-decoration:none}
|
||||
.skin-black .main-header .navbar{background-color:#111}.skin-black .main-header .navbar .nav>li>a{color:#fff}.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav>.active>a,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-black .main-header .navbar .sidebar-toggle{color:#fff}.skin-black .main-header .navbar .sidebar-toggle:hover{background-color:#040404}@media (max-width:767px){.skin-black .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-black .main-header .navbar .dropdown-menu li a{color:#fff}.skin-black .main-header .navbar .dropdown-menu li a:hover{background:#040404}}.skin-black .main-header li.user-header{background-color:#111}.skin-black .content-header{background:transparent}.skin-black .left-side,.skin-black .main-sidebar,.skin-black .wrapper{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#111}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .treeview-menu>li>a{color:#8aa4af}.skin-black .treeview-menu>li.active>a,.skin-black .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-black .sidebar-form .btn,.skin-black .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black .sidebar-form input[type=text]:focus,.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn,.btn:hover{text-decoration:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#505156;border-color:#b5bbc8;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#111;border-color:#1f1f21;color:#fff}.btn.btn-white:hover,.btn.btn-white:link,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:link,.btn:hover.btn-white:visited{color:#fff}a{color:#111;text-decoration:underline}a:hover{color:#000}a:visited{color:#111}.text-primary{color:#000}.skin-black .main-header .navbar .nav>li>a{text-decoration:none}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#111}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link,btn-sm.btn-primary,btn-sm .btn-primary:link{background-color:#307095;border-color:#23536f;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover,btn-sma.btn-primary:hover{background-color:#23536f;border-color:#23536f;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link,btn-sm.btn-white:link{background-color:#307095;color:#fff}.btn.btn-white:hover,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:visited,btn-sm.btn-white:hover,btn-sm.btn-white:visited{background-color:#173648;color:#fff}.btn-danger,.btn-danger:link,.btn-danger:visited,.btn-warning,.btn-warning:link,.btn-warning:visited,a.btn-danger:hover,a.btn-warning:hover{color:#fff}a{color:#3c8dbc}a:hover{color:#23536f}a:visited{color:#3c8dbc}.text-primary{color:#23536f}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link,btn-sm.btn-primary,btn-sm .btn-primary:link{background-color:#307095;border-color:#23536f;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover,btn-sma.btn-primary:hover{background-color:#23536f;border-color:#23536f;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link,btn-sm.btn-white:link{background-color:#307095;color:#fff}.btn.btn-white:hover,.btn.btn-white:visited,.btn:hover.btn-white:hover,.btn:hover.btn-white:visited,btn-sm.btn-white:hover,btn-sm.btn-white:visited{background-color:#173648;color:#fff}.btn-danger,.btn-danger:link,.btn-danger:visited,.btn-warning,.btn-warning:link,.btn-warning:visited,a.btn-danger:hover,a.btn-warning:hover{color:#fff}a{color:#3c8dbc}a:hover{color:#23536f}a:visited{color:#3c8dbc}.text-primary{color:#23536f}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
.skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav>.active>a,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .left-side,.skin-green .main-sidebar,.skin-green .wrapper{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li.active>a,.skin-green .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .treeview-menu>li>a{color:#8aa4af}.skin-green .treeview-menu>li.active>a,.skin-green .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-green .sidebar-form .btn,.skin-green .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-green .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type=text]:focus,.skin-green .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-green.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#00733e;border-color:#004023;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#004023;border-color:#004023;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#00733e;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#000d07;color:#fff}a{color:#00a65a}a:hover{color:#004023}a:visited{color:#00a65a}.text-primary{color:#004023}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav>.active>a,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .left-side,.skin-green .main-sidebar,.skin-green .wrapper{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li.active>a,.skin-green .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .treeview-menu>li>a{color:#8aa4af}.skin-green .treeview-menu>li.active>a,.skin-green .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-green .sidebar-form .btn,.skin-green .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-green .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type=text]:focus,.skin-green .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-green.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#00733e;border-color:#004023;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#004023;border-color:#004023;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#00733e;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#000d07;color:#fff}a{color:#00a65a}a:hover{color:#004023}a:visited{color:#00a65a}.text-primary{color:#004023}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#00a65a}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
.skin-orange .main-header .navbar{background-color:#ff8c00}.skin-orange .main-header .navbar .nav>li>a{color:#fff}.skin-orange .main-header .navbar .nav .open>a,.skin-orange .main-header .navbar .nav .open>a:focus,.skin-orange .main-header .navbar .nav .open>a:hover,.skin-orange .main-header .navbar .nav>.active>a,.skin-orange .main-header .navbar .nav>li>a:active,.skin-orange .main-header .navbar .nav>li>a:focus,.skin-orange .main-header .navbar .nav>li>a:hover,.skin-orange .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-orange .main-header .navbar .sidebar-toggle{color:#fff}.skin-orange .main-header .navbar .sidebar-toggle:hover{background-color:#e67e00}@media (max-width:767px){.skin-orange .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-orange .main-header .navbar .dropdown-menu li a{color:#fff}.skin-orange .main-header .navbar .dropdown-menu li a:hover{background:#e67e00}}.skin-orange .main-header li.user-header{background-color:#ff8c00}.skin-orange .content-header{background:transparent}.skin-orange .left-side,.skin-orange .main-sidebar,.skin-orange .wrapper{background-color:#222d32}.skin-orange .user-panel>.info,.skin-orange .user-panel>.info>a{color:#fff}.skin-orange .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-orange .sidebar-menu>li>a{border-left:3px solid transparent}.skin-orange .sidebar-menu>li.active>a,.skin-orange .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#ff8c00}.skin-orange .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-orange .sidebar a{color:#b8c7ce}.skin-orange .sidebar a:hover{text-decoration:none}.skin-orange .treeview-menu>li>a{color:#8aa4af}.skin-orange .treeview-menu>li.active>a,.skin-orange .treeview-menu>li>a:hover{color:#fff}.skin-orange .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-orange .sidebar-form .btn,.skin-orange .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-orange .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-orange .sidebar-form input[type=text]:focus,.skin-orange .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-orange .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-orange .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-orange.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#cc7000;border-color:#995400;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#995400;border-color:#995400;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#cc7000;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#663800;color:#fff}a{color:#ff8c00}a:hover{color:#995400}a:visited{color:#ff8c00}.text-primary{color:#995400}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-orange .main-header .navbar{background-color:#ff8c00}.skin-orange .main-header .navbar .nav>li>a{color:#fff}.skin-orange .main-header .navbar .nav .open>a,.skin-orange .main-header .navbar .nav .open>a:focus,.skin-orange .main-header .navbar .nav .open>a:hover,.skin-orange .main-header .navbar .nav>.active>a,.skin-orange .main-header .navbar .nav>li>a:active,.skin-orange .main-header .navbar .nav>li>a:focus,.skin-orange .main-header .navbar .nav>li>a:hover,.skin-orange .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-orange .main-header .navbar .sidebar-toggle{color:#fff}.skin-orange .main-header .navbar .sidebar-toggle:hover{background-color:#e67e00}@media (max-width:767px){.skin-orange .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-orange .main-header .navbar .dropdown-menu li a{color:#fff}.skin-orange .main-header .navbar .dropdown-menu li a:hover{background:#e67e00}}.skin-orange .main-header li.user-header{background-color:#ff8c00}.skin-orange .content-header{background:transparent}.skin-orange .left-side,.skin-orange .main-sidebar,.skin-orange .wrapper{background-color:#222d32}.skin-orange .user-panel>.info,.skin-orange .user-panel>.info>a{color:#fff}.skin-orange .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-orange .sidebar-menu>li>a{border-left:3px solid transparent}.skin-orange .sidebar-menu>li.active>a,.skin-orange .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#ff8c00}.skin-orange .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-orange .sidebar a{color:#b8c7ce}.skin-orange .sidebar a:hover{text-decoration:none}.skin-orange .treeview-menu>li>a{color:#8aa4af}.skin-orange .treeview-menu>li.active>a,.skin-orange .treeview-menu>li>a:hover{color:#fff}.skin-orange .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-orange .sidebar-form .btn,.skin-orange .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-orange .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-orange .sidebar-form input[type=text]:focus,.skin-orange .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-orange .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-orange .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-orange.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#cc7000;border-color:#995400;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#995400;border-color:#995400;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#cc7000;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#663800;color:#fff}a{color:#ff8c00}a:hover{color:#995400}a:visited{color:#ff8c00}.text-primary{color:#995400}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#ff8c00}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
.skin-purple .main-header .navbar{background-color:#605ca8}.skin-purple .main-header .navbar .nav>li>a{color:#fff}.skin-purple .main-header .navbar .nav .open>a,.skin-purple .main-header .navbar .nav .open>a:focus,.skin-purple .main-header .navbar .nav .open>a:hover,.skin-purple .main-header .navbar .nav>.active>a,.skin-purple .main-header .navbar .nav>li>a:active,.skin-purple .main-header .navbar .nav>li>a:focus,.skin-purple .main-header .navbar .nav>li>a:hover,.skin-purple .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-purple .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple .main-header li.user-header{background-color:#605ca8}.skin-purple .content-header{background:transparent}.skin-purple .left-side,.skin-purple .main-sidebar,.skin-purple .wrapper{background-color:#222d32}.skin-purple .user-panel>.info,.skin-purple .user-panel>.info>a{color:#fff}.skin-purple .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-purple .sidebar-menu>li>a{border-left:3px solid transparent}.skin-purple .sidebar-menu>li.active>a,.skin-purple .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#605ca8}.skin-purple .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-purple .sidebar a{color:#b8c7ce}.skin-purple .sidebar a:hover{text-decoration:none}.skin-purple .treeview-menu>li>a{color:#8aa4af}.skin-purple .treeview-menu>li.active>a,.skin-purple .treeview-menu>li>a:hover{color:#fff}.skin-purple .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-purple .sidebar-form .btn,.skin-purple .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-purple .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple .sidebar-form input[type=text]:focus,.skin-purple .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-purple.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#4c4988;border-color:#3a3767;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#3a3767;border-color:#3a3767;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#4c4988;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#272546;color:#fff}a{color:#605ca8}a:hover{color:#3a3767}a:visited{color:#605ca8}.text-primary{color:#3a3767}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-purple .main-header .navbar{background-color:#605ca8}.skin-purple .main-header .navbar .nav>li>a{color:#fff}.skin-purple .main-header .navbar .nav .open>a,.skin-purple .main-header .navbar .nav .open>a:focus,.skin-purple .main-header .navbar .nav .open>a:hover,.skin-purple .main-header .navbar .nav>.active>a,.skin-purple .main-header .navbar .nav>li>a:active,.skin-purple .main-header .navbar .nav>li>a:focus,.skin-purple .main-header .navbar .nav>li>a:hover,.skin-purple .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-purple .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple .main-header li.user-header{background-color:#605ca8}.skin-purple .content-header{background:transparent}.skin-purple .left-side,.skin-purple .main-sidebar,.skin-purple .wrapper{background-color:#222d32}.skin-purple .user-panel>.info,.skin-purple .user-panel>.info>a{color:#fff}.skin-purple .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-purple .sidebar-menu>li>a{border-left:3px solid transparent}.skin-purple .sidebar-menu>li.active>a,.skin-purple .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#605ca8}.skin-purple .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-purple .sidebar a{color:#b8c7ce}.skin-purple .sidebar a:hover{text-decoration:none}.skin-purple .treeview-menu>li>a{color:#8aa4af}.skin-purple .treeview-menu>li.active>a,.skin-purple .treeview-menu>li>a:hover{color:#fff}.skin-purple .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-purple .sidebar-form .btn,.skin-purple .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-purple .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple .sidebar-form input[type=text]:focus,.skin-purple .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-purple.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#4c4988;border-color:#3a3767;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#3a3767;border-color:#3a3767;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#4c4988;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#272546;color:#fff}a{color:#605ca8}a:hover{color:#3a3767}a:visited{color:#605ca8}.text-primary{color:#3a3767}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#605ca8}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
.skin-red .main-header .navbar{background-color:#dd4b39}.skin-red .main-header .navbar .nav>li>a{color:#fff}.skin-red .main-header .navbar .nav .open>a,.skin-red .main-header .navbar .nav .open>a:focus,.skin-red .main-header .navbar .nav .open>a:hover,.skin-red .main-header .navbar .nav>.active>a,.skin-red .main-header .navbar .nav>li>a:active,.skin-red .main-header .navbar .nav>li>a:focus,.skin-red .main-header .navbar .nav>li>a:hover,.skin-red .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-red .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red .main-header li.user-header{background-color:#dd4b39}.skin-red .content-header{background:transparent}.skin-red .left-side,.skin-red .main-sidebar,.skin-red .wrapper{background-color:#222d32}.skin-red .user-panel>.info,.skin-red .user-panel>.info>a{color:#fff}.skin-red .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-red .sidebar-menu>li>a{border-left:3px solid transparent}.skin-red .sidebar-menu>li.active>a,.skin-red .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#dd4b39}.skin-red .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-red .sidebar a{color:#b8c7ce}.skin-red .sidebar a:hover{text-decoration:none}.skin-red .treeview-menu>li>a{color:#8aa4af}.skin-red .treeview-menu>li.active>a,.skin-red .treeview-menu>li>a:hover{color:#fff}.skin-red .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-red .sidebar-form .btn,.skin-red .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-red .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red .sidebar-form input[type=text]:focus,.skin-red .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-red.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#c23321;border-color:#96271a;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#96271a;border-color:#96271a;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#c23321;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#6b1c12;color:#fff}a{color:#dd4b39}a:hover{color:#96271a}a:visited{color:#dd4b39}.text-primary{color:#96271a}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-red .main-header .navbar{background-color:#dd4b39}.skin-red .main-header .navbar .nav>li>a{color:#fff}.skin-red .main-header .navbar .nav .open>a,.skin-red .main-header .navbar .nav .open>a:focus,.skin-red .main-header .navbar .nav .open>a:hover,.skin-red .main-header .navbar .nav>.active>a,.skin-red .main-header .navbar .nav>li>a:active,.skin-red .main-header .navbar .nav>li>a:focus,.skin-red .main-header .navbar .nav>li>a:hover,.skin-red .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-red .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red .main-header li.user-header{background-color:#dd4b39}.skin-red .content-header{background:transparent}.skin-red .left-side,.skin-red .main-sidebar,.skin-red .wrapper{background-color:#222d32}.skin-red .user-panel>.info,.skin-red .user-panel>.info>a{color:#fff}.skin-red .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-red .sidebar-menu>li>a{border-left:3px solid transparent}.skin-red .sidebar-menu>li.active>a,.skin-red .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#dd4b39}.skin-red .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-red .sidebar a{color:#b8c7ce}.skin-red .sidebar a:hover{text-decoration:none}.skin-red .treeview-menu>li>a{color:#8aa4af}.skin-red .treeview-menu>li.active>a,.skin-red .treeview-menu>li>a:hover{color:#fff}.skin-red .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-red .sidebar-form .btn,.skin-red .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-red .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red .sidebar-form input[type=text]:focus,.skin-red .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-red.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#c23321;border-color:#96271a;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#96271a;border-color:#96271a;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#c23321;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#6b1c12;color:#fff}a{color:#dd4b39}a:hover{color:#96271a}a:visited{color:#dd4b39}.text-primary{color:#96271a}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#dd4b39}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
.skin-yellow .main-header .navbar{background-color:#f39c12}.skin-yellow .main-header .navbar .nav>li>a{color:#fff}.skin-yellow .main-header .navbar .nav .open>a,.skin-yellow .main-header .navbar .nav .open>a:focus,.skin-yellow .main-header .navbar .nav .open>a:hover,.skin-yellow .main-header .navbar .nav>.active>a,.skin-yellow .main-header .navbar .nav>li>a:active,.skin-yellow .main-header .navbar .nav>li>a:focus,.skin-yellow .main-header .navbar .nav>li>a:hover,.skin-yellow .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-yellow .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow .main-header li.user-header{background-color:#f39c12}.skin-yellow .content-header{background:transparent}.skin-yellow .left-side,.skin-yellow .main-sidebar,.skin-yellow .wrapper{background-color:#222d32}.skin-yellow .user-panel>.info,.skin-yellow .user-panel>.info>a{color:#fff}.skin-yellow .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-yellow .sidebar-menu>li>a{border-left:3px solid transparent}.skin-yellow .sidebar-menu>li.active>a,.skin-yellow .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#f39c12}.skin-yellow .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-yellow .sidebar a{color:#b8c7ce}.skin-yellow .sidebar a:hover{text-decoration:none}.skin-yellow .treeview-menu>li>a{color:#8aa4af}.skin-yellow .treeview-menu>li.active>a,.skin-yellow .treeview-menu>li>a:hover{color:#fff}.skin-yellow .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-yellow .sidebar-form .btn,.skin-yellow .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-yellow .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow .sidebar-form input[type=text]:focus,.skin-yellow .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-yellow.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#c87f0a;border-color:#976008;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#976008;border-color:#976008;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#c87f0a;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#674105;color:#fff}a{color:#f39c12}a:hover{color:#976008}a:visited{color:#f39c12}.text-primary{color:#976008}.fixed-table-container tbody .selected td{background-color:#fff8af}
|
||||
.skin-yellow .main-header .navbar{background-color:#f39c12}.skin-yellow .main-header .navbar .nav>li>a{color:#fff}.skin-yellow .main-header .navbar .nav .open>a,.skin-yellow .main-header .navbar .nav .open>a:focus,.skin-yellow .main-header .navbar .nav .open>a:hover,.skin-yellow .main-header .navbar .nav>.active>a,.skin-yellow .main-header .navbar .nav>li>a:active,.skin-yellow .main-header .navbar .nav>li>a:focus,.skin-yellow .main-header .navbar .nav>li>a:hover,.skin-yellow .main-header .navbar .sidebar-toggle:hover{background:rgba(0,0,0,.1);color:#f6f6f6}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow .main-header .navbar .dropdown-menu li.divider{background-color:hsla(0,0%,100%,.1)}.skin-yellow .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow .main-header li.user-header{background-color:#f39c12}.skin-yellow .content-header{background:transparent}.skin-yellow .left-side,.skin-yellow .main-sidebar,.skin-yellow .wrapper{background-color:#222d32}.skin-yellow .user-panel>.info,.skin-yellow .user-panel>.info>a{color:#fff}.skin-yellow .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-yellow .sidebar-menu>li>a{border-left:3px solid transparent}.skin-yellow .sidebar-menu>li.active>a,.skin-yellow .sidebar-menu>li:hover>a{color:#fff;background:#1e282c;border-left-color:#f39c12}.skin-yellow .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-yellow .sidebar a{color:#b8c7ce}.skin-yellow .sidebar a:hover{text-decoration:none}.skin-yellow .treeview-menu>li>a{color:#8aa4af}.skin-yellow .treeview-menu>li.active>a,.skin-yellow .treeview-menu>li>a:hover{color:#fff}.skin-yellow .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px}.skin-yellow .sidebar-form .btn,.skin-yellow .sidebar-form input[type=text]{-webkit-box-shadow:none;box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-yellow .sidebar-form input[type=text]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow .sidebar-form input[type=text]:focus,.skin-yellow .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-yellow.layout-top-nav .main-header>.logo .logo-variant{background-color:none}.btn.btn-primary,.btn .btn-primary:link,.btn:hover.btn-primary,.btn:hover .btn-primary:link{background-color:#c87f0a;border-color:#976008;color:#fff}.btn:hovera.btn-primary:hover,.btna.btn-primary:hover{background-color:#976008;border-color:#976008;color:#fff}.btn.btn-white:link,.btn:hover.btn-white:link{background-color:#c87f0a;color:#fff}.btn.btn-white:hover,.btn:hover.btn-white:hover{background-color:#674105;color:#fff}a{color:#f39c12}a:hover{color:#976008}a:visited{color:#f39c12}.text-primary{color:#976008}.fixed-table-container tbody .selected td{background-color:#fff8af}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#f39c12}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
File diff suppressed because one or more lines are too long
Vendored
+34
-34
File diff suppressed because one or more lines are too long
+23
-23
@@ -1,38 +1,38 @@
|
||||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=648e026d19aa24504e0f",
|
||||
"/js/build/app.js": "/js/build/app.js?id=dbeda4a18809e6247c2a",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=59413334823616b81341",
|
||||
"/css/build/app.css": "/css/build/app.css?id=032fd8c3fce99c7fd862",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=0b4aefd7ef0c117ef23a",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=9fa704134cfacfacab93",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=747948e5f269f64047f7",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=2a741dd5409582c0af89",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=817384969ed226028416",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=d7996d850e8bcdc4e167",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=eb25d2ec49f730d09431",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=eb4404a7b646ea42e025",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=35602987835e5d50d162",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=5789dd8af07b08034581",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=2e9f90ff200d4e9f45a8",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=b6dcb6d5c666fc5c8cc0",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=8150adf2e5f70ec3eb00",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=cb85a4e40e784319e878",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=5fc4a3cf9407c6a9d398",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=2f665cf40d7348b3f94c",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=5267e92a8df9ba833e01",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=b4fc4a74e1f6367dc3e2",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=eee1d10b6d7db576428e",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=3b3c544f99a912a4a27b",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=39250963ef83dac17f77",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=1a1ebcc437f2de698c27",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=9b0d44d6e3a9cf46fa02",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=b8be296ada5cbc4563fa",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=3acc03e5a20757aa08ca",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=67a30cff63473dade0e3",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=46311efe6c78ad08a7ce",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=1473bf8494b4c962eb44",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=b41e38b304805136e81f",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=39eb843599c275696aed",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=199fdf677ce0dce6cef8",
|
||||
"/css/blue.png": "/css/blue.png?id=4c85d6a97173123bd14a",
|
||||
"/css/blue@2x.png": "/css/blue@2x.png?id=62c67c6a822439e8a4ac",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=eb4404a7b646ea42e025",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=5789dd8af07b08034581",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=2f665cf40d7348b3f94c",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=5fc4a3cf9407c6a9d398",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=2e9f90ff200d4e9f45a8",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=8150adf2e5f70ec3eb00",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=5267e92a8df9ba833e01",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=3b3c544f99a912a4a27b",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=1a1ebcc437f2de698c27",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=1473bf8494b4c962eb44",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=46311efe6c78ad08a7ce",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=9b0d44d6e3a9cf46fa02",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=3acc03e5a20757aa08ca",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=b41e38b304805136e81f",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=d7996d850e8bcdc4e167",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced",
|
||||
"/css/build/signature-pad.min.css": "/css/build/signature-pad.min.css?id=d41d8cd98f00b204e980",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=1e77fde04b3f42432581",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=b93877b4a88a76e1b18b",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=58d95c93430f2ae33392",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=b4627a6533d841cd8fdf"
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=0efed60df4636f215a73"
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ $(document).ready(function () {
|
||||
};
|
||||
return data;
|
||||
},
|
||||
processResults: function (data, params) {
|
||||
/* processResults: function (data, params) {
|
||||
|
||||
params.page = params.page || 1;
|
||||
|
||||
@@ -224,12 +224,12 @@ $(document).ready(function () {
|
||||
};
|
||||
|
||||
return answer;
|
||||
},
|
||||
}, */
|
||||
cache: true
|
||||
},
|
||||
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
|
||||
templateResult: formatDatalist,
|
||||
templateSelection: formatDataSelection
|
||||
//escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
|
||||
templateResult: formatDatalistSafe,
|
||||
//templateSelection: formatDataSelection
|
||||
});
|
||||
|
||||
});
|
||||
@@ -331,12 +331,12 @@ $(document).ready(function () {
|
||||
return loading_markup;
|
||||
}
|
||||
|
||||
var markup = "<div class='clearfix'>" ;
|
||||
markup +="<div class='pull-left' style='padding-right: 10px;'>";
|
||||
var markup = '<div class="clearfix">' ;
|
||||
markup += '<div class="pull-left" style="padding-right: 10px;">';
|
||||
if (datalist.image) {
|
||||
markup += "<div style='width: 30px;'><img src='" + datalist.image + "' style='max-height: 20px; max-width: 30px;' alt='" + datalist.text + "'></div>";
|
||||
} else {
|
||||
markup += "<div style='height: 20px; width: 30px;'></div>";
|
||||
markup += '<div style="height: 20px; width: 30px;"></div>';
|
||||
}
|
||||
|
||||
markup += "</div><div>" + datalist.text + "</div>";
|
||||
@@ -344,6 +344,58 @@ $(document).ready(function () {
|
||||
return markup;
|
||||
}
|
||||
|
||||
function formatDatalistSafe(datalist) {
|
||||
// console.warn("What in the hell is going on with Select2?!?!!?!?");
|
||||
// console.warn($.select2);
|
||||
if (datalist.loading) {
|
||||
return $('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> Loading...');
|
||||
}
|
||||
|
||||
var root_div = $("<div class='clearfix'>") ;
|
||||
var left_pull = $("<div class='pull-left' style='padding-right: 10px;'>");
|
||||
if (datalist.image) {
|
||||
var inner_div = $("<div style='width: 30px;'>");
|
||||
/******************************************************************
|
||||
*
|
||||
* We are specifically chosing empty alt-text below, because this
|
||||
* image conveys no additional information, relative to the text
|
||||
* that will *always* be there in any select2 list that is in use
|
||||
* in Snipe-IT. If that changes, we would probably want to change
|
||||
* some signatures of some functions, but right now, we don't want
|
||||
* screen readers to say "HP SuperJet 5000, .... picture of HP
|
||||
* SuperJet 5000..." and so on, for every single row in a list of
|
||||
* assets or models or whatever.
|
||||
*
|
||||
*******************************************************************/
|
||||
var img = $("<img src='' style='max-height: 20px; max-width: 30px;' alt=''>");
|
||||
// console.warn("Img is: ");
|
||||
// console.dir(img);
|
||||
// console.warn("Strigularly, that's: ");
|
||||
// console.log(img);
|
||||
img.attr("src", datalist.image );
|
||||
inner_div.append(img)
|
||||
} else {
|
||||
var inner_div=$("<div style='height: 20px; width: 30px;'></div>");
|
||||
}
|
||||
left_pull.append(inner_div);
|
||||
root_div.append(left_pull);
|
||||
var name_div = $("<div>");
|
||||
name_div.text(datalist.text);
|
||||
root_div.append(name_div)
|
||||
var safe_html = root_div.get(0).outerHTML;
|
||||
var old_html = formatDatalist(datalist);
|
||||
if(safe_html != old_html) {
|
||||
console.log("HTML MISMATCH: ");
|
||||
console.log("FormatDatalistSafe: ");
|
||||
// console.dir(root_div.get(0));
|
||||
console.log(safe_html);
|
||||
console.log("FormatDataList: ");
|
||||
console.log(old_html);
|
||||
}
|
||||
return root_div;
|
||||
|
||||
}
|
||||
|
||||
function formatDataSelection (datalist) {
|
||||
// This a heinous workaround for a known bug in Select2.
|
||||
// Without this, the rich selectlists are vulnerable to XSS.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user