mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-09 23:24:06 -08:00
Merge branch 'develop' into feature/sc-15014/asset-acceptance-and-signed-eula-as-pdf
This commit is contained in:
commit
fe6a6740db
|
@ -2514,7 +2514,7 @@
|
|||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
{
|
||||
"login": "Haxatron",
|
||||
"name": "Haxatron",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/76475453?v=4",
|
||||
|
@ -2541,7 +2541,7 @@
|
|||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
{
|
||||
"login": "UniversalSuperBox",
|
||||
"name": "Dalton Durst",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/21966173?v=4",
|
||||
|
|
|
@ -157,4 +157,4 @@ IMPORT_TIME_LIMIT=600
|
|||
IMPORT_MEMORY_LIMIT=500M
|
||||
REPORT_TIME_LIMIT=12000
|
||||
REQUIRE_SAML=false
|
||||
|
||||
API_THROTTLE_PER_MINUTE=120
|
||||
|
|
|
@ -41,6 +41,7 @@ libmcrypt-dev \
|
|||
php7.4-dev \
|
||||
ca-certificates \
|
||||
unzip \
|
||||
dnsutils \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -1,5 +1,5 @@
|
|||
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-282-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-284-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
|
@ -128,10 +128,10 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
|||
| [<img src="https://avatars.githubusercontent.com/u/9255772?v=4" width="110px;"/><br /><sub>Mark Stenglein</sub>](https://markstenglein.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ocelotsloth "Code") | [<img src="https://avatars.githubusercontent.com/u/35658596?v=4" width="110px;"/><br /><sub>ajsy</sub>](https://github.com/ajsy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ajsy "Code") | [<img src="https://avatars.githubusercontent.com/u/3628035?v=4" width="110px;"/><br /><sub>Jan Kiesewetter</sub>](https://github.com/t3easy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=t3easy "Code") | [<img src="https://avatars.githubusercontent.com/u/79449630?v=4" width="110px;"/><br /><sub>Tetrachloromethane250</sub>](https://github.com/Tetrachloromethane250)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tetrachloromethane250 "Code") | [<img src="https://avatars.githubusercontent.com/u/22004482?v=4" width="110px;"/><br /><sub>Lars Kajes</sub>](https://www.kajes.se/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kajes "Code") | [<img src="https://avatars.githubusercontent.com/u/13993216?v=4" width="110px;"/><br /><sub>Joly0</sub>](https://github.com/Joly0)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Joly0 "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>theburger</sub>](https://github.com/limeless)<br />[💻](https://github.com/snipe/snipe-it/commits?author=limeless "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/36065681?v=4" width="110px;"/><br /><sub>David Valin Alonso</sub>](https://github.com/deivishome)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deivishome "Code") | [<img src="https://avatars.githubusercontent.com/u/8290389?v=4" width="110px;"/><br /><sub>andreaci</sub>](https://github.com/andreaci)<br />[💻](https://github.com/snipe/snipe-it/commits?author=andreaci "Code") | [<img src="https://avatars.githubusercontent.com/u/1828542?v=4" width="110px;"/><br /><sub>Jelle Sebreghts</sub>](http://www.jellesebreghts.be)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jelle-S "Code") | [<img src="https://avatars.githubusercontent.com/u/11180862?v=4" width="110px;"/><br /><sub>Michael Pietsch</sub>](https://github.com/Skywalker-11)<br /> | [<img src="https://avatars.githubusercontent.com/u/22068886?v=4" width="110px;"/><br /><sub>Masudul Haque Shihab</sub>](https://github.com/sh1hab)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sh1hab "Code") | [<img src="https://avatars.githubusercontent.com/u/16099942?v=4" width="110px;"/><br /><sub>Supapong Areeprasertkul</sub>](http://www.freedomdive.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zybersup "Code") | [<img src="https://avatars.githubusercontent.com/u/207358?v=4" width="110px;"/><br /><sub>Peter Sarossy</sub>](https://github.com/psarossy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=psarossy "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/11823649?v=4" width="110px;"/><br /><sub>Renee Margaret McConahy</sub>](https://github.com/nepella)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nepella "Code") | [<img src="https://avatars.githubusercontent.com/u/5553884?v=4" width="110px;"/><br /><sub>JohnnyPicnic</sub>](https://github.com/JohnnyPicnic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JohnnyPicnic "Code") | [<img src="https://avatars.githubusercontent.com/u/8799594?v=4" width="110px;"/><br /><sub>markbrule</sub>](https://github.com/markbrule)<br />[💻](https://github.com/snipe/snipe-it/commits?author=markbrule "Code") | [<img src="https://avatars.githubusercontent.com/u/1962801?v=4" width="110px;"/><br /><sub>Mike Campbell</sub>](https://github.com/mikecmpbll)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikecmpbll "Code") | [<img src="https://avatars.githubusercontent.com/u/11973217?v=4" width="110px;"/><br /><sub>tbrconnect</sub>](https://github.com/tbrconnect)<br />[💻](https://github.com/snipe/snipe-it/commits?author=tbrconnect "Code") | [<img src="https://avatars.githubusercontent.com/u/12447225?v=4" width="110px;"/><br /><sub>kcoyo</sub>](https://github.com/kcoyo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kcoyo "Code") | [<img src="https://avatars.githubusercontent.com/u/494017?v=4" width="110px;"/><br /><sub>Travis Miller</sub>](https://travismiller.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=travismiller "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") | [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") | [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/917232?v=4" width="110px;"/><br /><sub>Simona Avornicesei</sub>](http://www.avornicesei.com)<br />[⚠️](https://github.com/snipe/snipe-it/commits?author=savornicesei "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
59
app/Console/Commands/KillAllSessions.php
Normal file
59
app/Console/Commands/KillAllSessions.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class KillAllSessions extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:global-logout {--force : Skip the danger prompt; assuming you enter "y"} ';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This command will destroy all web sessions on disk and will force a re-login for all users.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if (!$this->option('force') && !$this->confirm("****************************************************\nTHIS WILL FORCE A LOGIN FOR ALL LOGGED IN USERS.\n\nAre you SURE you wish to continue? ")) {
|
||||
return $this->error("Session loss not confirmed");
|
||||
}
|
||||
|
||||
$session_files = glob(storage_path("framework/sessions/*"));
|
||||
|
||||
$count = 0;
|
||||
foreach ($session_files as $file) {
|
||||
|
||||
if (is_file($file))
|
||||
unlink($file);
|
||||
$count++;
|
||||
}
|
||||
\DB::table('users')->update(['remember_token' => null]);
|
||||
|
||||
$this->info($count. ' sessions cleared!');
|
||||
|
||||
}
|
||||
}
|
503
app/Console/Commands/LdapTroubleshooter.php
Normal file
503
app/Console/Commands/LdapTroubleshooter.php
Normal file
|
@ -0,0 +1,503 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Setting;
|
||||
use Exception;
|
||||
use Crypt;
|
||||
|
||||
/**
|
||||
* Check if a given ip is in a network
|
||||
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
|
||||
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
|
||||
* @return boolean true if the ip is in this range / false if not.
|
||||
*/
|
||||
function ip_in_range( $ip, $range ) {
|
||||
if ( strpos( $range, '/' ) == false ) {
|
||||
$range .= '/32';
|
||||
}
|
||||
// $range is in IP/CIDR format eg 127.0.0.1/24
|
||||
list( $range, $netmask ) = explode( '/', $range, 2 );
|
||||
$range_decimal = ip2long( $range );
|
||||
$ip_decimal = ip2long( $ip );
|
||||
$wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
|
||||
$netmask_decimal = ~ $wildcard_decimal;
|
||||
return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
|
||||
}
|
||||
// NOTE - this function was shamelessly stolen from this gist: https://gist.github.com/tott/7684443
|
||||
|
||||
class LdapTroubleshooter extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ldap:troubleshoot
|
||||
{--ldap-search : Output an ldapsearch command-line for testing your LDAP config}
|
||||
{--force : Skip the interactive yes/no prompt for confirmation}
|
||||
{--debug : Include debugging output (verbose)}
|
||||
{--trace : Include extremely verbose LDAP trace output}
|
||||
{--timeout=15 : Timeout for LDAP Bind operations}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs a series of non-destructive LDAP commands to help try and determine correct LDAP settings for your environment.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output something *only* if debug is enabled
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debugout($string)
|
||||
{
|
||||
if($this->option('debug')) {
|
||||
$this->line($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if($this->option('trace')) {
|
||||
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
$this->settings = $settings;
|
||||
if($this->option('ldap-search')) {
|
||||
if(!$this->option('force')) {
|
||||
$confirmation = $this->confirm('WARNING: This command will display your LDAP password on your terminal. Are you sure this is ok?');
|
||||
if(!$confirmation) {
|
||||
$this->error('ABORTING');
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
$output = [];
|
||||
if($settings->ldap_server_cert_ignore) {
|
||||
$this->line("# Ignoring server certificate validity");
|
||||
$output[] = "LDAPTLS_REQCERT=never";
|
||||
}
|
||||
if($settings->ldap_client_tls_cert && $settings->ldap_client_tls_key) {
|
||||
$this->line("# Adding LDAP Client Certificate and Key");
|
||||
$output[] = "LDAPTLS_CERT=storage/ldap_client_tls.cert";
|
||||
$output[] = "LDAPTLS_KEY=storage/ldap_client_tls.key";
|
||||
}
|
||||
$output[] = "ldapsearch";
|
||||
$output[] = $settings->ldap_server;
|
||||
$output[] = "-x";
|
||||
$output[] = "-b ".escapeshellarg($settings->ldap_basedn);
|
||||
$output[] = "-D ".escapeshellarg($settings->ldap_uname);
|
||||
$output[] = "-w ".escapeshellarg(\Crypt::Decrypt($settings->ldap_pword));
|
||||
if(substr($settings->ldap_filter,0,1) == "(" ) {
|
||||
$output[] = escapeshellarg($settings->ldap_filter);
|
||||
} else {
|
||||
$output[] = escapeshellarg("(".$settings->ldap_filter.")");
|
||||
}
|
||||
if($settings->ldap_tls) {
|
||||
$this->line("# adding STARTTLS option");
|
||||
$output[] = "-Z";
|
||||
}
|
||||
$output[] = "-v";
|
||||
$this->line("\n");
|
||||
$this->line(implode(" \\\n",$output));
|
||||
exit(0);
|
||||
}
|
||||
if(!$this->option('force')) {
|
||||
$confirmation = $this->confirm('WARNING: This command will make several attempts to connect to your LDAP server. Are you sure this is ok?');
|
||||
if(!$confirmation) {
|
||||
$this->error('ABORTING');
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
//$this->line(print_r($settings,true));
|
||||
$this->info("STAGE 1: Checking settings");
|
||||
if(!$settings->ldap_enabled) {
|
||||
$this->error("WARNING: Snipe-IT's LDAP setting is not turned on. (That may be OK if you're still trying to figure out settings)");
|
||||
}
|
||||
|
||||
$ldap_conn = false;
|
||||
try {
|
||||
$ldap_conn = ldap_connect($settings->ldap_server);
|
||||
} catch (Exception $e) {
|
||||
$this->error("WARNING: Exception caught when executing 'ldap_connect()' - ".$e->getMessage().". We will try to guess.");
|
||||
}
|
||||
|
||||
if(!$ldap_conn) {
|
||||
$this->error("WARNING: LDAP Server setting of: ".$settings->ldap_server." cannot be parsed. We will try to guess.");
|
||||
//exit(-1);
|
||||
}
|
||||
//since we never use $ldap_conn again, we don't have to ldap_unbind() it (it's not even connected, tbh - that only happens at bind-time)
|
||||
|
||||
$parsed = parse_url($settings->ldap_server);
|
||||
|
||||
if(@$parsed['scheme'] != 'ldap' && @$parsed['scheme'] != 'ldaps') {
|
||||
$this->error("WARNING: LDAP URL Scheme of '".@$parsed['scheme']."' is probably incorrect; should usually be ldap or ldaps");
|
||||
}
|
||||
|
||||
if(!@$parsed['host']) {
|
||||
$this->error("ERROR: Cannot determine hostname or IP from ldap URL: ".$settings->ldap_server.". ABORTING.");
|
||||
exit(-1);
|
||||
} else {
|
||||
$this->info("Determined LDAP hostname to be: ".$parsed['host']);
|
||||
}
|
||||
|
||||
$this->info("Performing DNS lookup of: ".$parsed['host']);
|
||||
$ips = dns_get_record($parsed['host']);
|
||||
$raw_ips = [];
|
||||
|
||||
//$this->info("Host IP is: ".print_r($ips,true));
|
||||
|
||||
if(!$ips || count($ips) == 0) {
|
||||
$this->error("ERROR: DNS lookup of host: ".$parsed['host']." has failed. ABORTING.");
|
||||
exit(-1);
|
||||
}
|
||||
$this->debugout("IP's? ".print_r($ips,true));
|
||||
foreach($ips as $ip) {
|
||||
if(!isset($ip['ip'])) {
|
||||
continue;
|
||||
}
|
||||
$raw_ips[]=$ip['ip'];
|
||||
if($ip['ip'] == "127.0.0.1") {
|
||||
$this->error("WARNING: Using the localhost IP as the LDAP server. This is usually wrong");
|
||||
}
|
||||
if(ip_in_range($ip['ip'],'10.0.0.0/8') || ip_in_range($ip['ip'],'192.168.0.0/16') || ip_in_range($ip['ip'], '172.16.0.0/12')) {
|
||||
$this->error("WARNING: Using an RFC1918 Private address for LDAP server. This may be correct, but it can be a problem if your Snipe-IT instance is not hosted on your private network");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("STAGE 2: Checking basic network connectivity");
|
||||
$ports = [389,636];
|
||||
if(@$parsed['port'] && !in_array($parsed['port'],$ports)) {
|
||||
$ports[] = $parsed['port'];
|
||||
}
|
||||
|
||||
$open_ports=[];
|
||||
foreach($ports as $port ) {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
$timeout = 30.0;
|
||||
$result = '';
|
||||
$this->info("Attempting to connect to port: ".$port." - may take up to $timeout seconds");
|
||||
try {
|
||||
$result = fsockopen($parsed['host'], $port, $errno, $errstr, 30.0);
|
||||
} catch(Exception $e) {
|
||||
$this->error("Exception: ".$e->getMessage());
|
||||
}
|
||||
if($result) {
|
||||
$this->info("Success!");
|
||||
$open_ports[] = $port;
|
||||
} else {
|
||||
$this->error("WARNING: Cannot connect to port: $port - $errstr ($errno)");
|
||||
}
|
||||
}
|
||||
|
||||
if(count($open_ports) == 0) {
|
||||
$this->error("ERROR - no open ports. ABORTING.");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
$this->info("STAGE 3: Determine encryption algorithm, if any");
|
||||
|
||||
$ldap_urls = [];
|
||||
$pretty_ldap_urls = [];
|
||||
foreach($open_ports as $port) {
|
||||
$this->line("Trying TLS first for port $port");
|
||||
$ldap_url = "ldaps://".$parsed['host'].":$port";
|
||||
if($this->test_anonymous_bind($ldap_url)) {
|
||||
$this->info("Anonymous bind succesful to $ldap_url!");
|
||||
$ldap_urls[] = [ $ldap_url, true, false ];
|
||||
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
|
||||
continue; // TODO - lots of copypasta in these if(test_anonymous_bind()) routines...
|
||||
} else {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url - trying without certificate checks.");
|
||||
}
|
||||
|
||||
if($this->test_anonymous_bind($ldap_url, false)) {
|
||||
$this->info("Anonymous bind succesful to $ldap_url with certifcate-checks disabled");
|
||||
$ldap_urls[] = [ $ldap_url, false, false ];
|
||||
$pretty_ldap_urls[] = [ $ldap_url, "no", "no" ];
|
||||
continue;
|
||||
} else {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url with certificate checks disabled. Trying unencrypted with STARTTLS");
|
||||
}
|
||||
|
||||
$ldap_url = "ldap://".$parsed['host'].":$port";
|
||||
if($this->test_anonymous_bind($ldap_url, true, true)) {
|
||||
$this->info("Plain connection to $ldap_url with STARTTLS succesful!");
|
||||
$ldap_urls[] = [ $ldap_url, true, true ];
|
||||
$pretty_ldap_urls[] = [ $ldap_url, "YES", "YES" ];
|
||||
continue;
|
||||
} else {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url with STARTTLS enabled. Trying without STARTTLS");
|
||||
}
|
||||
|
||||
if($this->test_anonymous_bind($ldap_url)) {
|
||||
$this->info("Plain connection to $ldap_url succesful!");
|
||||
$ldap_urls[] = [ $ldap_url, true, false ];
|
||||
$pretty_ldap_urls[] = [ $ldap_url, "YES", "no" ];
|
||||
continue;
|
||||
} else {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url. Giving up on port $port");
|
||||
}
|
||||
}
|
||||
|
||||
$this->debugout(print_r($ldap_urls,true));
|
||||
|
||||
if(count($ldap_urls) > 0 ) {
|
||||
$this->info("Found working LDAP URL's: ");
|
||||
foreach($ldap_urls as $ldap_url) { // TODO maybe do this as a $this->table() instead?
|
||||
$this->info("LDAP URL: ".$ldap_url[0]);
|
||||
$this->info($ldap_url[0]. ($ldap_url[1] ? " certificate checks enabled" : " certificate checks disabled"). ($ldap_url[2] ? " STARTTLS Enabled ": " STARTTLS Disabled"));
|
||||
}
|
||||
$this->table(["URL", "Cert Checks Enabled?", "STARTTLS Enabled?"],$pretty_ldap_urls);
|
||||
} else {
|
||||
$this->error("ERROR - no valid LDAP URL's available - ABORTING");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$this->info("STAGE 4: Test Administrative Bind for LDAP Sync");
|
||||
foreach($ldap_urls AS $ldap_url) {
|
||||
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $settings->ldap_uname, Crypt::decrypt($settings->ldap_pword));
|
||||
}
|
||||
|
||||
$this->info("STAGE 5: Test BaseDN");
|
||||
//grab all LDAP_ constants and fill up a reversed array mapping from weird LDAP dotted-strings to (Constant Name)
|
||||
$all_defined_constants = get_defined_constants();
|
||||
$ldap_constants = [];
|
||||
foreach($all_defined_constants AS $key => $val) {
|
||||
if(starts_with($key,"LDAP_") && is_string($val)) {
|
||||
$ldap_constants[$val] = $key; // INVERT the meaning here!
|
||||
}
|
||||
}
|
||||
$this->debugout("LDAP constants are: ".print_r($ldap_constants,true));
|
||||
|
||||
// recursive function that 'cleans' the returned array from ldap_get_entries which are formatted awfully
|
||||
$cleaner = function ($array) {
|
||||
$cleaned = [];
|
||||
for($i = 0; $i < $array['count']; $i++) {
|
||||
$row = $array[$i];
|
||||
$clean_row = [];
|
||||
foreach($row AS $key => $val ) {
|
||||
$this->debugout("Key is: ".$key);
|
||||
if($key == "count" || is_int($key) || $key == "dn") {
|
||||
$this->debugout(" and we're gonna skip it\n");
|
||||
continue;
|
||||
}
|
||||
$this->debugout(" And that seems fine.\n");
|
||||
if(array_key_exists('count',$val)) {
|
||||
if($val['count'] == 1) {
|
||||
$clean_row[$key] = $val[0];
|
||||
} else {
|
||||
unset($val['count']); //these counts are annoying
|
||||
$elements = [];
|
||||
foreach($val as $entry) {
|
||||
if(isset($ldap_constants[$entry])) {
|
||||
$elements[] = $ldap_constants[$entry];
|
||||
} else {
|
||||
$elements[] = $entry;
|
||||
}
|
||||
}
|
||||
$clean_row[$key] = $elements;
|
||||
}
|
||||
} else {
|
||||
$clean_row[$key] = $val;
|
||||
}
|
||||
}
|
||||
$cleaned[$i] = $clean_row;
|
||||
}
|
||||
return $cleaned;
|
||||
};
|
||||
|
||||
foreach($ldap_urls AS $ldap_url) {
|
||||
if($this->test_informational_bind($ldap_url[0],$ldap_url[1],$ldap_url[2],$settings->ldap_uname,Crypt::decrypt($settings->ldap_pword))) {
|
||||
$this->info("Success getting informational bind!");
|
||||
} else {
|
||||
$this->error("Unable to get information from bind.");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("STAGE 6: Test LDAP Login to Snipe-IT");
|
||||
foreach($ldap_urls AS $ldap_url) {
|
||||
$this->info("Starting auth to ".$ldap_url[0]);
|
||||
while(true) {
|
||||
$with_tls = $ldap_url[1] ? "with": "without";
|
||||
$with_startssl = $ldap_url[2] ? "using": "not using";
|
||||
if(!$this->confirm('Do you wish to try to authenticate to this directory: '.$ldap_url[0]." $with_tls TLS and $with_startssl STARTSSL?")) {
|
||||
break;
|
||||
}
|
||||
$username = $this->ask("Username");
|
||||
$password = $this->secret("Password");
|
||||
$this->test_authed_bind($ldap_url[0], $ldap_url[1], $ldap_url[2], $username, $password); // FIXME - should do some other stuff here, maybe with the concatenating or something? maybe? and/or should put up some results?
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("LDAP TROUBLESHOOTING COMPLETE!");
|
||||
}
|
||||
|
||||
public function connect_to_ldap($ldap_url, $check_cert, $start_tls)
|
||||
{
|
||||
$lconn = ldap_connect($ldap_url);
|
||||
ldap_set_option($lconn, LDAP_OPT_PROTOCOL_VERSION, 3); // should we 'test' different protocol versions here? Does anyone even use anything other than LDAPv3?
|
||||
// no - it's formally deprecated: https://tools.ietf.org/html/rfc3494
|
||||
if(!$check_cert) {
|
||||
putenv('LDAPTLS_REQCERT=never'); // This is horrible; is this *really* the only way to do it?
|
||||
} else {
|
||||
putenv('LDAPTLS_REQCERT'); // have to very explicitly and manually *UN* set the env var here to ensure it works
|
||||
}
|
||||
if($this->settings->ldap_client_tls_cert && $this->settings->ldap_client_tls_key) {
|
||||
// client-side TLS certificate support for LDAP (Google Secure LDAP)
|
||||
putenv('LDAPTLS_CERT=storage/ldap_client_tls.cert');
|
||||
putenv('LDAPTLS_KEY=storage/ldap_client_tls.key');
|
||||
}
|
||||
if($start_tls) {
|
||||
if(!ldap_start_tls($lconn)) {
|
||||
$this->error("WARNING: Unable to start TLS");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!$lconn) {
|
||||
$this->error("WARNING: Failed to generate connection string - using: ".$ldap_url);
|
||||
return false;
|
||||
}
|
||||
$net = ldap_set_option($lconn, LDAP_OPT_NETWORK_TIMEOUT, $this->option('timeout'));
|
||||
$time = ldap_set_option($lconn, LDAP_OPT_TIMELIMIT, $this->option('timeout'));
|
||||
if(!$net || !$time) {
|
||||
$this->error("Unable to set timeouts!");
|
||||
}
|
||||
return $lconn;
|
||||
}
|
||||
|
||||
public function test_anonymous_bind($ldap_url, $check_cert = true, $start_tls = false)
|
||||
{
|
||||
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert , $start_tls) {
|
||||
try {
|
||||
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
|
||||
$this->info("gonna try to bind now, this can take a while if we mess it up");
|
||||
$bind_results = ldap_bind($lconn);
|
||||
$this->info("Bind results are: ".$bind_results." which translate into boolean: ".(bool)$bind_results);
|
||||
return (bool)$bind_results;
|
||||
} catch (Exception $e) {
|
||||
$this->error("WARNING: Exception caught during bind - ".$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function test_authed_bind($ldap_url, $check_cert, $start_tls, $username, $password)
|
||||
{
|
||||
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password) {
|
||||
try {
|
||||
$lconn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
|
||||
$bind_results = ldap_bind($lconn, $username, $password);
|
||||
if(!$bind_results) {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url as $username");
|
||||
return false;
|
||||
} else {
|
||||
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
|
||||
return (bool)$lconn;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function test_informational_bind($ldap_url, $check_cert, $start_tls, $username, $password)
|
||||
{
|
||||
return $this->timed_boolean_execute(function () use ($ldap_url, $check_cert, $start_tls, $username, $password) {
|
||||
try { // TODO - copypasta'ed from test_authed_bind
|
||||
$conn = $this->connect_to_ldap($ldap_url, $check_cert, $start_tls);
|
||||
$bind_results = ldap_bind($conn, $username, $password);
|
||||
if(!$bind_results) {
|
||||
$this->error("WARNING: Failed to bind to $ldap_url as $username");
|
||||
return false;
|
||||
}
|
||||
$this->info("SUCCESS - Able to bind to $ldap_url as $username");
|
||||
$result = ldap_read($conn, '', '(objectClass=*)'/* , ['supportedControl']*/);
|
||||
$results = ldap_get_entries($conn, $result);
|
||||
$cleaned_results = $cleaner($results);
|
||||
$this->line(print_r($cleaned_results,true));
|
||||
//okay, great - now how do we display those results? I have no idea.
|
||||
// I don't see why this throws an Exception for Google LDAP, but I guess we ought to try and catch it?
|
||||
$this->comment("I guess we're trying to do the ldap search here, but sometimes it takes too long?");
|
||||
$search_results = ldap_search($conn, $settings->base_dn, $settings->filter);
|
||||
$this->info("Printing first 10 results: ");
|
||||
for($i=0;$i<10;$i++) {
|
||||
$this->info($search_results[$i]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error("WARNING: Exception caught during Authed bind to $username - ".$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
*
|
||||
* This function executes $function - which is expected to be some kind of executable function -
|
||||
* with a timeout set. It respects the timeout by forking execution and setting a strict timer
|
||||
* for which to get back a SIGUSR1 or SIGUSR2 signal from the forked process.
|
||||
*
|
||||
***********************************************/
|
||||
private function timed_boolean_execute($function)
|
||||
{
|
||||
if(!(function_exists('pcntl_sigtimedwait') && function_exists('posix_getpid') && function_exists('pcntl_fork') && function_exists('posix_kill') && function_exists('pcntl_wifsignaled'))) {
|
||||
// POSIX functions needed for forking aren't present, just run the function inline (ignoring timeout)
|
||||
$this->info('WARNING: Unable to execute POSIX fork() commands, timeout may not be respected');
|
||||
return $function();
|
||||
} else {
|
||||
$parent_pid = posix_getpid();
|
||||
$pid = pcntl_fork();
|
||||
switch($pid) {
|
||||
case 0:
|
||||
//we're the 'child'
|
||||
if($function()) {
|
||||
//SUCCESS = SIGUSR1
|
||||
posix_kill($parent_pid, SIGUSR1);
|
||||
} else {
|
||||
//FAILURE = SIGUSR2
|
||||
posix_kill($parent_pid, SIGUSR2);
|
||||
}
|
||||
exit();
|
||||
break; //yes I know we don't need it.
|
||||
case -1:
|
||||
//couldn't fork
|
||||
$this->error("COULD NOT FORK - assuming failure");
|
||||
return false;
|
||||
break; //I still know that we don't need it
|
||||
default:
|
||||
//we remain the 'parent', $pid is the PID of the forked process.
|
||||
$siginfo = [];
|
||||
$exit_status = pcntl_sigtimedwait ([SIGUSR1, SIGUSR2], $siginfo, $this->option('timeout'));
|
||||
if ($exit_status == SIGUSR1) {
|
||||
return true;
|
||||
} else {
|
||||
posix_kill($pid, SIGKILL); //make sure we don't have processes hanging around that might try and send signals during later executions, confusing us
|
||||
return false;
|
||||
}
|
||||
break; //Yeah I get it already, shush.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -82,6 +82,7 @@ class RestoreFromBackup extends Command
|
|||
return $this->error('Could not access file: '.$filename.' - '.array_key_exists($errcode, $errors) ? $errors[$errcode] : " Unknown reason: $errcode");
|
||||
}
|
||||
|
||||
|
||||
$private_dirs = [
|
||||
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
|
||||
'storage/private_uploads/audits',
|
||||
|
@ -245,19 +246,21 @@ class RestoreFromBackup extends Command
|
|||
return false;
|
||||
}
|
||||
$bytes_read = 0;
|
||||
|
||||
while (($buffer = fgets($sql_contents, self::$buffer_size)) !== false) {
|
||||
$bytes_read += strlen($buffer);
|
||||
// \Log::debug("Buffer is: '$buffer'");
|
||||
$bytes_written = fwrite($pipes[0], $buffer);
|
||||
|
||||
if ($bytes_written === false) {
|
||||
$stdout = fgets($pipes[1]);
|
||||
$this->info($stdout);
|
||||
$stderr = fgets($pipes[2]);
|
||||
$this->info($stderr);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!feof($sql_contents) || $bytes_read == 0) {
|
||||
return $this->error("Not at end of file for sql file, or zero bytes read. aborting!");
|
||||
}
|
||||
|
|
|
@ -84,10 +84,12 @@ class Handler extends ExceptionHandler
|
|||
switch ($e->getStatusCode()) {
|
||||
case '404':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
|
||||
case '405':
|
||||
case '429':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
|
||||
case '405':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
|
||||
default:
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), 405);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ class AccessoriesController extends Controller
|
|||
$accessory->qty = request('qty');
|
||||
$accessory->user_id = Auth::user()->id;
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory->notes = request('notes');
|
||||
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
|
@ -143,6 +145,7 @@ class AccessoriesController extends Controller
|
|||
$accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory->notes = request('notes');
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
|
|
|
@ -10,12 +10,22 @@ class ActionlogController extends Controller
|
|||
{
|
||||
public function displaySig($filename)
|
||||
{
|
||||
// PHP doesn't let you handle file not found errors well with
|
||||
// file_get_contents, so we set the error reporting for just this class
|
||||
error_reporting(0);
|
||||
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
$file = config('app.private_uploads').'/signatures/'.$filename;
|
||||
$filetype = Helper::checkUploadIsImage($file);
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
return Response::make($contents)->header('Content-Type', $filetype);
|
||||
$contents = file_get_contents($file, false, stream_context_create(['http' => ['ignore_errors' => true]]));
|
||||
if ($contents === false) {
|
||||
\Log::warn('File '.$file.' not found');
|
||||
return false;
|
||||
} else {
|
||||
return Response::make($contents)->header('Content-Type', $filetype);
|
||||
}
|
||||
|
||||
}
|
||||
public function getStoredEula($filename){
|
||||
$this->authorize('view', \App\Models\Asset::class);
|
||||
|
|
|
@ -40,7 +40,8 @@ class AccessoriesController extends Controller
|
|||
'notes',
|
||||
'created_at',
|
||||
'min_amt',
|
||||
'company_id'
|
||||
'company_id',
|
||||
'notes',
|
||||
];
|
||||
|
||||
|
||||
|
@ -70,6 +71,10 @@ class AccessoriesController extends Controller
|
|||
$accessories->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$accessories->where('notes','=',$request->input('notes'));
|
||||
}
|
||||
|
||||
// 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.
|
||||
$offset = (($accessories) && ($request->get('offset') > $accessories->count())) ? $accessories->count() : $request->get('offset', 0);
|
||||
|
|
|
@ -102,7 +102,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
|
@ -154,7 +154,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
|
@ -218,7 +218,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function destroy($assetMaintenanceId)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
|
|
|
@ -521,7 +521,7 @@ class AssetsController extends Controller
|
|||
$asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost')); // this is the API's store method, so I don't know that I want to do this? Confusing. FIXME (or not?!)
|
||||
$asset->purchase_date = $request->get('purchase_date', null);
|
||||
$asset->assigned_to = $request->get('assigned_to', null);
|
||||
$asset->supplier_id = $request->get('supplier_id', 0);
|
||||
$asset->supplier_id = $request->get('supplier_id');
|
||||
$asset->requestable = $request->get('requestable', 0);
|
||||
$asset->rtd_location_id = $request->get('rtd_location_id', null);
|
||||
$asset->location_id = $request->get('rtd_location_id', null);
|
||||
|
|
|
@ -40,6 +40,7 @@ class ComponentsController extends Controller
|
|||
'purchase_cost',
|
||||
'qty',
|
||||
'image',
|
||||
'notes',
|
||||
];
|
||||
|
||||
|
||||
|
@ -62,6 +63,10 @@ class ComponentsController extends Controller
|
|||
$components->where('location_id', '=', $request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$components->where('notes','=',$request->input('notes'));
|
||||
}
|
||||
|
||||
// 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.
|
||||
$offset = (($components) && ($request->get('offset') > $components->count())) ? $components->count() : $request->get('offset', 0);
|
||||
|
|
|
@ -42,6 +42,7 @@ class ConsumablesController extends Controller
|
|||
'item_no',
|
||||
'qty',
|
||||
'image',
|
||||
'notes',
|
||||
];
|
||||
|
||||
|
||||
|
@ -74,6 +75,10 @@ class ConsumablesController extends Controller
|
|||
$consumables->where('location_id','=',$request->input('location_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$consumables->where('notes','=',$request->input('notes'));
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -30,6 +30,20 @@ class StatuslabelsController extends Controller
|
|||
$statuslabels = $statuslabels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
// if a status_type is passed, filter by that
|
||||
if ($request->filled('status_type')) {
|
||||
if (strtolower($request->input('status_type'))== 'pending') {
|
||||
$statuslabels = $statuslabels->Pending();
|
||||
} elseif (strtolower($request->input('status_type'))== 'archived') {
|
||||
$statuslabels = $statuslabels->Archived();
|
||||
} elseif (strtolower($request->input('status_type'))== 'deployable') {
|
||||
$statuslabels = $statuslabels->Deployable();
|
||||
} elseif (strtolower($request->input('status_type'))== 'undeployable') {
|
||||
$statuslabels = $statuslabels->Undeployable();
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
$offset = (($statuslabels) && ($request->get('offset') > $statuslabels->count())) ? $statuslabels->count() : $request->get('offset', 0);
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
|
|||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\UsersTransformer;
|
||||
|
@ -62,6 +63,7 @@ class UsersController extends Controller
|
|||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
'users.remote',
|
||||
'users.ldap_import',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables')
|
||||
|
@ -131,6 +133,30 @@ class UsersController extends Controller
|
|||
$users = $users->where('users.manager_id','=',$request->input('manager_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('ldap_import')) {
|
||||
$users = $users->where('ldap_import', '=', $request->input('ldap_import'));
|
||||
}
|
||||
|
||||
if ($request->filled('remote')) {
|
||||
$users = $users->where('remote', '=', $request->input('remote'));
|
||||
}
|
||||
|
||||
if ($request->filled('assets_count')) {
|
||||
$users->has('assets', '=', $request->input('assets_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('consumables_count')) {
|
||||
$users->has('consumables', '=', $request->input('consumables_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('licenses_count')) {
|
||||
$users->has('licenses', '=', $request->input('licenses_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('accessories_count')) {
|
||||
$users->has('accessories', '=', $request->input('accessories_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
|
@ -166,7 +192,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', 'ldap_import',
|
||||
'country', 'zip', 'id', 'ldap_import', 'remote',
|
||||
];
|
||||
|
||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||
|
@ -445,6 +471,24 @@ class UsersController extends Controller
|
|||
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of consumables assigned to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @param $userId
|
||||
* @return string JSON
|
||||
*/
|
||||
public function consumables(Request $request, $id)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
$this->authorize('view', Consumable::class);
|
||||
$user = User::findOrFail($id);
|
||||
$consumables = $user->consumables;
|
||||
return (new ConsumablesTransformer)->transformConsumables($consumables, $consumables->count(), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON containing a list of accessories assigned to a user.
|
||||
*
|
||||
|
|
|
@ -65,7 +65,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
$asset = null;
|
||||
|
||||
if ($asset = Asset::find(request('asset_id'))) {
|
||||
|
@ -96,7 +96,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
|
@ -148,7 +148,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function edit($assetMaintenanceId = null)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the improvement management page
|
||||
|
@ -199,7 +199,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
|
@ -267,7 +267,7 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function destroy($assetMaintenanceId)
|
||||
{
|
||||
$this->authorize('edit', Asset::class);
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
|
|
|
@ -111,6 +111,13 @@ class AssetCheckinController extends Controller
|
|||
$checkin_at = $request->input('checkin_at');
|
||||
}
|
||||
|
||||
if(!empty($asset->licenseseats->all())){
|
||||
foreach ($asset->licenseseats as $seat){
|
||||
$seat->assigned_to = null;
|
||||
$seat->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Get all pending Acceptances for this asset and delete them
|
||||
$acceptances = CheckoutAcceptance::pending()->whereHasMorph('checkoutable',
|
||||
[Asset::class],
|
||||
|
|
|
@ -80,6 +80,15 @@ class AssetCheckoutController extends Controller
|
|||
$asset->status_id = $request->get('status_id');
|
||||
}
|
||||
|
||||
if(!empty($asset->licenseseats->all())){
|
||||
if(request('checkout_to_type') == 'user') {
|
||||
foreach ($asset->licenseseats as $seat){
|
||||
$seat->assigned_to = $target->id;
|
||||
$seat->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
|
|
|
@ -137,12 +137,12 @@ class AssetsController extends Controller
|
|||
$asset->archived = '0';
|
||||
$asset->physical = '1';
|
||||
$asset->depreciate = '0';
|
||||
$asset->status_id = request('status_id', 0);
|
||||
$asset->status_id = request('status_id');
|
||||
$asset->warranty_months = request('warranty_months', null);
|
||||
$asset->purchase_cost = Helper::ParseCurrency($request->get('purchase_cost'));
|
||||
$asset->purchase_date = request('purchase_date', null);
|
||||
$asset->assigned_to = request('assigned_to', null);
|
||||
$asset->supplier_id = request('supplier_id', 0);
|
||||
$asset->supplier_id = request('supplier_id', null);
|
||||
$asset->requestable = request('requestable', 0);
|
||||
$asset->rtd_location_id = request('rtd_location_id', null);
|
||||
|
||||
|
@ -235,6 +235,7 @@ class AssetsController extends Controller
|
|||
->with('statuslabel_types', Helper::statusTypeList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that presents information about an asset for detail view.
|
||||
*
|
||||
|
@ -309,6 +310,7 @@ class AssetsController extends Controller
|
|||
$asset->location_id = $request->input('rtd_location_id', null);
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('image_delete')) {
|
||||
try {
|
||||
unlink(public_path().'/uploads/assets/'.$asset->image);
|
||||
|
@ -401,6 +403,24 @@ class AssetsController extends Controller
|
|||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the assets table by serial, and redirects if it finds one
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function getAssetBySerial(Request $request)
|
||||
{
|
||||
$topsearch = ($request->get('topsearch')=="true");
|
||||
|
||||
if (!$asset = Asset::where('serial', '=', $request->get('serial'))->first()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
}
|
||||
$this->authorize('view', $asset);
|
||||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the assets table by asset tag, and redirects if it finds one
|
||||
*
|
||||
|
@ -420,6 +440,7 @@ class AssetsController extends Controller
|
|||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a QR code for the asset
|
||||
*
|
||||
|
@ -792,6 +813,7 @@ class AssetsController extends Controller
|
|||
return view('hardware/audit-overdue');
|
||||
}
|
||||
|
||||
|
||||
public function auditStore(Request $request, $id)
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
@ -822,6 +844,7 @@ class AssetsController extends Controller
|
|||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
|
||||
if ($asset->save()) {
|
||||
$file_name = '';
|
||||
// Upload an image, if attached
|
||||
|
@ -838,7 +861,7 @@ class AssetsController extends Controller
|
|||
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
|
||||
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,12 +68,13 @@ class LoginController extends Controller
|
|||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
//If the environment is set to ALWAYS require SAML, go straight to the SAML route.
|
||||
//We don't need to check other settings, as this should override those.
|
||||
if(config('app.require_saml')) {
|
||||
// If the environment is set to ALWAYS require SAML, go straight to the SAML route.
|
||||
// We don't need to check other settings, as this should override those.
|
||||
if (config('app.require_saml')) {
|
||||
return redirect()->route('saml.login');
|
||||
}
|
||||
|
||||
|
||||
if ($this->saml->isEnabled() && Setting::getSettings()->saml_forcelogin == '1' && ! ($request->has('nosaml') || $request->session()->has('error'))) {
|
||||
return redirect()->route('saml.login');
|
||||
}
|
||||
|
@ -235,8 +236,9 @@ class LoginController extends Controller
|
|||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
|
||||
//If the environment is set to ALWAYS require SAML, return access denied
|
||||
if(config('app.require_saml')) {
|
||||
if (config('app.require_saml')) {
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ class ComponentsController extends Controller
|
|||
$component->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost', null));
|
||||
$component->qty = $request->input('qty');
|
||||
$component->user_id = Auth::id();
|
||||
$component->notes = $request->input('notes');
|
||||
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
|
@ -152,6 +153,7 @@ class ComponentsController extends Controller
|
|||
$component->purchase_date = $request->input('purchase_date');
|
||||
$component->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$component->qty = $request->input('qty');
|
||||
$component->notes = $request->input('notes');
|
||||
|
||||
$component = $request->handleImages($component);
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ class ConsumablesController extends Controller
|
|||
$consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$consumable->qty = $request->input('qty');
|
||||
$consumable->user_id = Auth::id();
|
||||
$consumable->notes = $request->input('notes');
|
||||
|
||||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
|
@ -140,6 +142,7 @@ class ConsumablesController extends Controller
|
|||
$consumable->purchase_date = $request->input('purchase_date');
|
||||
$consumable->purchase_cost = Helper::ParseCurrency($request->input('purchase_cost'));
|
||||
$consumable->qty = Helper::ParseFloat($request->input('qty'));
|
||||
$consumable->notes = $request->input('notes');
|
||||
|
||||
$consumable = $request->handleImages($consumable);
|
||||
|
||||
|
|
|
@ -90,7 +90,9 @@ class BulkUsersController extends Controller
|
|||
->conditionallyAddItem('department_id')
|
||||
->conditionallyAddItem('company_id')
|
||||
->conditionallyAddItem('locale')
|
||||
->conditionallyAddItem('remote')
|
||||
->conditionallyAddItem('activated');
|
||||
|
||||
// If the manager_id is one of the users being updated, generate a warning.
|
||||
if (array_search($request->input('manager_id'), $user_raw_array)) {
|
||||
$manager_conflict = true;
|
||||
|
|
|
@ -93,8 +93,8 @@ class UsersController extends Controller
|
|||
$this->authorize('create', User::class);
|
||||
$user = new User;
|
||||
//Username, email, and password need to be handled specially because the need to respect config values on an edit.
|
||||
$user->email = e($request->input('email'));
|
||||
$user->username = e($request->input('username'));
|
||||
$user->email = trim($request->input('email'));
|
||||
$user->username = trim($request->input('username'));
|
||||
if ($request->filled('password')) {
|
||||
$user->password = bcrypt($request->input('password'));
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ class UsersController extends Controller
|
|||
$user->state = $request->input('state', null);
|
||||
$user->country = $request->input('country', null);
|
||||
$user->zip = $request->input('zip', null);
|
||||
$user->remote = $request->input('remote', 0);
|
||||
|
||||
// Strip out the superuser permission if the user isn't a superadmin
|
||||
$permissions_array = $request->input('permission');
|
||||
|
@ -179,7 +180,6 @@ class UsersController extends Controller
|
|||
if ($user = User::find($id)) {
|
||||
$this->authorize('update', $user);
|
||||
$permissions = config('permissions');
|
||||
|
||||
$groups = Group::pluck('name', 'id');
|
||||
|
||||
$userGroups = $user->groups()->pluck('name', 'id');
|
||||
|
@ -190,9 +190,7 @@ class UsersController extends Controller
|
|||
return view('users/edit', compact('user', 'groups', 'userGroups', 'permissions', 'userPermissions'))->with('item', $user);
|
||||
}
|
||||
|
||||
$error = trans('admin/users/message.user_not_found', compact('id'));
|
||||
|
||||
return redirect()->route('users.index')->with('error', $error);
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', compact('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -245,9 +243,9 @@ class UsersController extends Controller
|
|||
|
||||
// Update the user
|
||||
if ($request->filled('username')) {
|
||||
$user->username = $request->input('username');
|
||||
$user->username = trim($request->input('username'));
|
||||
}
|
||||
$user->email = $request->input('email');
|
||||
$user->email = trim($request->input('email'));
|
||||
$user->first_name = $request->input('first_name');
|
||||
$user->last_name = $request->input('last_name');
|
||||
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
|
||||
|
@ -267,6 +265,7 @@ class UsersController extends Controller
|
|||
$user->country = $request->input('country', null);
|
||||
$user->activated = $request->input('activated', 0);
|
||||
$user->zip = $request->input('zip', null);
|
||||
$user->remote = $request->input('remote', 0);
|
||||
|
||||
// Update the location of any assets checked out to this user
|
||||
Asset::where('assigned_type', User::class)
|
||||
|
@ -623,7 +622,7 @@ class UsersController extends Controller
|
|||
public function sendPasswordReset($id)
|
||||
{
|
||||
if (($user = User::find($id)) && ($user->activated == '1') && ($user->email != '') && ($user->ldap_import == '0')) {
|
||||
$credentials = ['email' => $user->email];
|
||||
$credentials = ['email' => trim($user->email)];
|
||||
|
||||
try {
|
||||
\Password::sendResetLink($credentials, function (Message $message) use ($user) {
|
||||
|
|
|
@ -45,7 +45,6 @@ class Kernel extends HttpKernel
|
|||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:120,1',
|
||||
'auth:api',
|
||||
],
|
||||
];
|
||||
|
|
|
@ -42,30 +42,15 @@ class SecurityHeaders
|
|||
// - https://github.com/w3c/webappsec-feature-policy/issues/189
|
||||
|
||||
$feature_policy[] = "accelerometer 'none'";
|
||||
$feature_policy[] = "ambient-light-sensor 'none'";
|
||||
$feature_policy[] = "animations 'none'";
|
||||
$feature_policy[] = "autoplay 'none'";
|
||||
$feature_policy[] = "battery 'none'";
|
||||
$feature_policy[] = "camera 'none'";
|
||||
$feature_policy[] = "display-capture 'none'";
|
||||
$feature_policy[] = "document-domain 'none'";
|
||||
$feature_policy[] = "encrypted-media 'none'";
|
||||
$feature_policy[] = "fullscreen 'none'";
|
||||
$feature_policy[] = "geolocation 'none'";
|
||||
$feature_policy[] = "gyroscope 'none'";
|
||||
$feature_policy[] = "legacy-image-formats 'none'";
|
||||
$feature_policy[] = "magnetometer 'none'";
|
||||
$feature_policy[] = "microphone 'none'";
|
||||
$feature_policy[] = "midi 'none'";
|
||||
$feature_policy[] = "oversized-images 'none'";
|
||||
$feature_policy[] = "payment 'none'";
|
||||
$feature_policy[] = "picture-in-picture 'none'";
|
||||
$feature_policy[] = "publickey-credentials 'none'";
|
||||
$feature_policy[] = "sync-xhr 'none'";
|
||||
$feature_policy[] = "unsized-media 'none'";
|
||||
$feature_policy[] = "usb 'none'";
|
||||
$feature_policy[] = "vibrate 'none'";
|
||||
$feature_policy[] = "wake-lock 'none'";
|
||||
$feature_policy[] = "xr-spatial-tracking 'none'";
|
||||
|
||||
$feature_policy = implode(';', $feature_policy);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
|
@ -9,73 +8,58 @@ use Illuminate\Database\Eloquent\Collection;
|
|||
|
||||
class ActionlogsTransformer
|
||||
{
|
||||
public function transformActionlogs(Collection $actionlogs, $total)
|
||||
|
||||
public function transformActionlogs (Collection $actionlogs, $total)
|
||||
{
|
||||
$array = [];
|
||||
$array = array();
|
||||
$settings = Setting::getSettings();
|
||||
foreach ($actionlogs as $actionlog) {
|
||||
$array[] = self::transformActionlog($actionlog, $settings);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformActionlog(Actionlog $actionlog, $settings = null)
|
||||
private function clean_field($value)
|
||||
{
|
||||
// This object stuff is weird, and is used to make up for the fact that
|
||||
// older data can get strangely formatted if an asset existed,
|
||||
// then a new custom field is added, and the asset is saved again.
|
||||
// It can result in funnily-formatted strings like:
|
||||
//
|
||||
// {"_snipeit_right_sized_fault_tolerant_localareanetwo_1":
|
||||
// {"old":null,"new":{"value":"1579490695972","_snipeit_new_field_2":2,"_snipeit_new_field_3":"Monday, 20 January 2020 2:24:55 PM"}}
|
||||
// so we have to walk down that next level
|
||||
if(is_object($value) && isset($value->value)) {
|
||||
return $this->clean_field($value->value);
|
||||
}
|
||||
return is_scalar($value) || is_null($value) ? e($value) : e(json_encode($value));
|
||||
}
|
||||
|
||||
public function transformActionlog (Actionlog $actionlog, $settings = null)
|
||||
{
|
||||
$icon = $actionlog->present()->icon();
|
||||
if ($actionlog->filename != '') {
|
||||
$icon = e(Helper::filetype_icon($actionlog->filename));
|
||||
if ($actionlog->filename!='') {
|
||||
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
|
||||
}
|
||||
|
||||
// This is necessary since we can't escape special characters within a JSON object
|
||||
if (($actionlog->log_meta) && ($actionlog->log_meta != '')) {
|
||||
if (($actionlog->log_meta) && ($actionlog->log_meta!='')) {
|
||||
$meta_array = json_decode($actionlog->log_meta);
|
||||
|
||||
if ($meta_array) {
|
||||
foreach ($meta_array as $key => $value) {
|
||||
foreach ($value as $meta_key => $meta_value) {
|
||||
if (is_array($meta_value)) {
|
||||
foreach ($meta_value as $meta_value_key => $meta_value_value) {
|
||||
if (is_scalar($meta_value_value)) {
|
||||
$clean_meta[$key][$meta_value_key] = e($meta_value_value);
|
||||
} else {
|
||||
$clean_meta[$key][$meta_value_key] = 'invalid scalar: '.print_r($meta_value_value, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// This object stuff is weird, and is used to make up for the fact that
|
||||
// older data can get strangely formatted if an asset existed,
|
||||
// then a new custom field is added, and the asset is saved again.
|
||||
// It can result in funnily-formatted strings like:
|
||||
//
|
||||
// {"_snipeit_right_sized_fault_tolerant_localareanetwo_1":
|
||||
// {"old":null,"new":{"value":"1579490695972","_snipeit_new_field_2":2,"_snipeit_new_field_3":"Monday, 20 January 2020 2:24:55 PM"}}
|
||||
// so we have to walk down that next level
|
||||
|
||||
if (is_object($meta_value)) {
|
||||
foreach ($meta_value as $meta_value_key => $meta_value_value) {
|
||||
if ($meta_value_key == 'value') {
|
||||
$clean_meta[$key]['old'] = null;
|
||||
$clean_meta[$key]['new'] = e($meta_value->value);
|
||||
} else {
|
||||
$clean_meta[$meta_value_key]['old'] = null;
|
||||
$clean_meta[$meta_value_key]['new'] = e($meta_value_value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$clean_meta[$key][$meta_key] = e($meta_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($meta_array as $fieldname => $fieldata) {
|
||||
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
|
||||
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$array = [
|
||||
'id' => (int) $actionlog->id,
|
||||
'icon' => $icon,
|
||||
'file' => ($actionlog->filename != '') ?
|
||||
'file' => ($actionlog->filename!='') ?
|
||||
[
|
||||
'url' => route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]),
|
||||
'filename' => $actionlog->filename,
|
||||
|
@ -84,7 +68,7 @@ class ActionlogsTransformer
|
|||
|
||||
'item' => ($actionlog->item) ? [
|
||||
'id' => (int) $actionlog->item->id,
|
||||
'name' => ($actionlog->itemType() == 'user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
|
||||
'name' => ($actionlog->itemType()=='user') ? $actionlog->filename : e($actionlog->item->getDisplayNameAttribute()),
|
||||
'type' => e($actionlog->itemType()),
|
||||
] : null,
|
||||
'location' => ($actionlog->location) ? [
|
||||
|
@ -93,18 +77,18 @@ class ActionlogsTransformer
|
|||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($actionlog->updated_at, 'datetime'),
|
||||
'next_audit_date' => ($actionlog->itemType() == 'asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(null, $actionlog->item), 'date') : null,
|
||||
'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->calcNextAuditDate(null, $actionlog->item), 'date'): null,
|
||||
'days_to_next_audit' => $actionlog->daysUntilNextAudit($settings->audit_interval, $actionlog->item),
|
||||
'action_type' => $actionlog->present()->actionType(),
|
||||
'admin' => ($actionlog->user) ? [
|
||||
'id' => (int) $actionlog->user->id,
|
||||
'name' => e($actionlog->user->getFullNameAttribute()),
|
||||
'first_name'=> e($actionlog->user->first_name),
|
||||
'last_name'=> e($actionlog->user->last_name),
|
||||
'last_name'=> e($actionlog->user->last_name)
|
||||
] : null,
|
||||
'target' => ($actionlog->target) ? [
|
||||
'id' => (int) $actionlog->target->id,
|
||||
'name' => ($actionlog->targetType() == 'user') ? e($actionlog->target->getFullNameAttribute()) : e($actionlog->target->getDisplayNameAttribute()),
|
||||
'name' => ($actionlog->targetType()=='user') ? e($actionlog->target->getFullNameAttribute()) : e($actionlog->target->getDisplayNameAttribute()),
|
||||
'type' => e($actionlog->targetType()),
|
||||
] : null,
|
||||
|
||||
|
@ -114,17 +98,23 @@ class ActionlogsTransformer
|
|||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||
'stored_eula_file' => ($actionlog->stored_eula_file) ? route('log.storedeula.download', ['filename' => $actionlog->stored_eula_file]) : null,
|
||||
];
|
||||
//\Log::info("Clean Meta is: ".print_r($clean_meta,true));
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutActionlog(Collection $accessories_users, $total)
|
||||
|
||||
|
||||
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
$array = array();
|
||||
foreach ($accessories_users as $user) {
|
||||
$array[] = (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace App\Http\Transformers;
|
|||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
|
@ -21,6 +22,9 @@ class AssetsTransformer
|
|||
|
||||
public function transformAsset(Asset $asset)
|
||||
{
|
||||
// This uses the getSettings() method so we're pulling from the cache versus querying the settings on single asset
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
$array = [
|
||||
'id' => (int) $asset->id,
|
||||
'name' => e($asset->name),
|
||||
|
@ -65,6 +69,8 @@ class AssetsTransformer
|
|||
'name'=> e($asset->defaultLoc->name),
|
||||
] : null,
|
||||
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
|
||||
'qr' => ($setting->qr_code=='1') ? config('app.url').'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png' : null,
|
||||
'alt_barcode' => ($setting->alt_barcode_enabled=='1') ? config('app.url').'/uploads/barcodes/'.str_slug($setting->alt_barcode).'-'.str_slug($asset->asset_tag).'.png' : null,
|
||||
'assigned_to' => $this->transformAssignedTo($asset),
|
||||
'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months.' '.trans('admin/hardware/form.months')) : null,
|
||||
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
|
||||
|
|
|
@ -45,6 +45,7 @@ class ComponentsTransformer
|
|||
'id' => (int) $component->company->id,
|
||||
'name' => e($component->company->name),
|
||||
] : null,
|
||||
'notes' => ($component->notes) ? e($component->notes) : null,
|
||||
'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'),
|
||||
'user_can_checkout' => ($component->numRemaining() > 0) ? 1 : 0,
|
||||
|
|
|
@ -38,6 +38,7 @@ class ConsumablesTransformer
|
|||
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
|
||||
'purchase_date' => Helper::getFormattedDateObject($consumable->purchase_date, 'date'),
|
||||
'qty' => (int) $consumable->qty,
|
||||
'notes' => ($consumable->notes) ? e($consumable->notes) : null,
|
||||
'created_at' => Helper::getFormattedDateObject($consumable->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($consumable->updated_at, 'datetime'),
|
||||
];
|
||||
|
|
|
@ -28,6 +28,7 @@ class UsersTransformer
|
|||
'first_name' => e($user->first_name),
|
||||
'last_name' => e($user->last_name),
|
||||
'username' => e($user->username),
|
||||
'remote' => ($user->remote == '1') ? true : false,
|
||||
'locale' => ($user->locale) ? e($user->locale) : null,
|
||||
'employee_num' => e($user->employee_num),
|
||||
'manager' => ($user->manager) ? [
|
||||
|
|
|
@ -39,6 +39,7 @@ class AssetImporter extends ItemImporter
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
$this->createAssetIfNotExists($row);
|
||||
}
|
||||
|
||||
|
@ -68,7 +69,7 @@ class AssetImporter extends ItemImporter
|
|||
$this->log('No Matching Asset, Creating a new one');
|
||||
$asset = new Asset;
|
||||
}
|
||||
|
||||
$this->item['notes'] = $this->findCsvMatch($row, 'asset_notes');
|
||||
$this->item['image'] = $this->findCsvMatch($row, 'image');
|
||||
$this->item['requestable'] = $this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'));
|
||||
$asset->requestable = $this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'));
|
||||
|
@ -96,10 +97,12 @@ class AssetImporter extends ItemImporter
|
|||
$item['rtd_location_id'] = $this->item['location_id'];
|
||||
}
|
||||
|
||||
$item['last_audit_date'] = null;
|
||||
if (isset($this->item['last_audit_date'])) {
|
||||
$item['last_audit_date'] = $this->item['last_audit_date'];
|
||||
}
|
||||
|
||||
$item['next_audit_date'] = null;
|
||||
if (isset($this->item['next_audit_date'])) {
|
||||
$item['next_audit_date'] = $this->item['next_audit_date'];
|
||||
}
|
||||
|
|
|
@ -221,6 +221,7 @@ class ItemImporter extends Importer
|
|||
$this->log('Matching Model found, updating it.');
|
||||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
$item['name'] = $asset_model_name;
|
||||
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
|
||||
|
||||
if(!empty($asset_modelNumber)){
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
|
@ -238,6 +239,7 @@ class ItemImporter extends Importer
|
|||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
$item['name'] = $asset_model_name;
|
||||
$item['model_number'] = $asset_modelNumber;
|
||||
$item['notes'] = $this->findCsvMatch($row, 'model_notes');
|
||||
|
||||
$asset_model->fill($item);
|
||||
$item = null;
|
||||
|
|
|
@ -47,14 +47,22 @@ class LicenseImporter extends ItemImporter
|
|||
$license = new License;
|
||||
}
|
||||
$asset_tag = $this->item['asset_tag'] = $this->findCsvMatch($row, 'asset_tag'); // used for checkout out to an asset.
|
||||
$this->item['expiration_date'] = $this->findCsvMatch($row, 'expiration_date');
|
||||
|
||||
$this->item["expiration_date"] = null;
|
||||
if ($this->findCsvMatch($row, "expiration_date")!='') {
|
||||
$this->item["expiration_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "expiration_date")));
|
||||
}
|
||||
$this->item['license_email'] = $this->findCsvMatch($row, 'license_email');
|
||||
$this->item['license_name'] = $this->findCsvMatch($row, 'license_name');
|
||||
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
|
||||
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
|
||||
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
|
||||
$this->item['seats'] = $this->findCsvMatch($row, 'seats');
|
||||
$this->item['termination_date'] = $this->findCsvMatch($row, 'termination_date');
|
||||
|
||||
$this->item["termination_date"] = null;
|
||||
if ($this->findCsvMatch($row, "termination_date")!='') {
|
||||
$this->item["termination_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "termination_date")));
|
||||
}
|
||||
|
||||
if ($editingLicense) {
|
||||
$license->update($this->sanitizeItemForUpdating($license));
|
||||
|
|
|
@ -37,7 +37,7 @@ class Accessory extends SnipeModel
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date'];
|
||||
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date', 'notes'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
@ -64,6 +64,7 @@ class Accessory extends SnipeModel
|
|||
'purchase_cost' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Whether the model should inject it's identifier to the unique
|
||||
* validation rules before attempting validation. If this property
|
||||
|
@ -94,6 +95,7 @@ class Accessory extends SnipeModel
|
|||
'qty',
|
||||
'min_amt',
|
||||
'requestable',
|
||||
'notes',
|
||||
];
|
||||
|
||||
|
||||
|
@ -110,6 +112,7 @@ class Accessory extends SnipeModel
|
|||
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the requestable attribute on the accessory
|
||||
*
|
||||
|
@ -220,8 +223,8 @@ class Accessory extends SnipeModel
|
|||
if ($this->image) {
|
||||
return Storage::disk('public')->url(app('accessories_upload_path').$this->image);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -196,6 +196,12 @@ class Asset extends Depreciable
|
|||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
$this->rules += $model->fieldset->validation_rules();
|
||||
|
||||
foreach ($this->model->fieldset->fields as $field){
|
||||
if($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ class Component extends SnipeModel
|
|||
'order_number',
|
||||
'qty',
|
||||
'serial',
|
||||
'notes',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
@ -74,7 +75,7 @@ class Component extends SnipeModel
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date'];
|
||||
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date', 'notes'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
|
|
@ -27,7 +27,8 @@ class Consumable extends SnipeModel
|
|||
'category_id' => 'integer',
|
||||
'company_id' => 'integer',
|
||||
'qty' => 'integer',
|
||||
'min_amt' => 'integer', ];
|
||||
'min_amt' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Category validation rules
|
||||
|
@ -70,6 +71,7 @@ class Consumable extends SnipeModel
|
|||
'qty',
|
||||
'min_amt',
|
||||
'requestable',
|
||||
'notes',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
@ -79,7 +81,7 @@ class Consumable extends SnipeModel
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number'];
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number', 'notes'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
|
|
@ -9,6 +9,6 @@ class AdminRecipient extends Recipient
|
|||
public function __construct()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$this->email = $settings->admin_cc_email;
|
||||
$this->email = trim($settings->admin_cc_email);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,18 @@ class Statuslabel extends SnipeModel
|
|||
->where('deployable', '=', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for undeployable status types
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeUndeployable()
|
||||
{
|
||||
return $this->where('pending', '=', 0)
|
||||
->where('archived', '=', 0)
|
||||
->where('deployable', '=', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to determine type attributes
|
||||
*
|
||||
|
|
|
@ -58,6 +58,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
|||
'state',
|
||||
'username',
|
||||
'zip',
|
||||
'remote',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
@ -59,7 +59,9 @@ class AccessoryPresenter extends Presenter
|
|||
'field' => 'manufacturer',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.manufacturer'),
|
||||
'visible' => false,
|
||||
'formatter' => 'manufacturersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'supplier',
|
||||
|
@ -89,6 +91,7 @@ class AccessoryPresenter extends Presenter
|
|||
'field' => 'remaining_qty',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
], [
|
||||
'field' => 'purchase_date',
|
||||
|
@ -110,6 +113,13 @@ class AccessoryPresenter extends Presenter
|
|||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.order_number'),
|
||||
],[
|
||||
'field' => 'notes',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter'
|
||||
], [
|
||||
'field' => 'change',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -103,6 +103,13 @@ class ComponentPresenter extends Presenter
|
|||
'visible' => true,
|
||||
'footerFormatter' => 'sumFormatterQuantity',
|
||||
'class' => 'text-right',
|
||||
], [
|
||||
'field' => 'notes',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter',
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
@ -115,6 +115,13 @@ class ConsumablePresenter extends Presenter
|
|||
'visible' => true,
|
||||
'footerFormatter' => 'sumFormatterQuantity',
|
||||
'class' => 'text-right',
|
||||
], [
|
||||
'field' => 'notes',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter',
|
||||
], [
|
||||
'field' => 'change',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -85,6 +85,15 @@ class UserPresenter extends Presenter
|
|||
'visible' => true,
|
||||
'formatter' => 'usersLinkFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'remote',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/users/general.remote'),
|
||||
'visible' => false,
|
||||
'formatter' => 'trueFalseFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'email',
|
||||
'searchable' => true,
|
||||
|
|
|
@ -121,6 +121,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------
|
||||
// Reports
|
||||
// -----------------------------------------
|
||||
|
@ -174,12 +175,21 @@ class AuthServiceProvider extends ServiceProvider
|
|||
// to the logged in API user, but creating assets, licenses, etc won't work
|
||||
// if the user can't view and interact with the select lists.
|
||||
Gate::define('view.selectlists', function ($user) {
|
||||
return $user->can(['create','update'], Asset::class)
|
||||
|| $user->can(['create','update'], License::class)
|
||||
|| $user->can(['create','update'], Component::class)
|
||||
|| $user->can(['create','update'], Consumable::class)
|
||||
|| $user->can(['create','update'], Accessory::class)
|
||||
|| $user->can(['create','update'], User::class);
|
||||
return $user->can('update', Asset::class)
|
||||
|| $user->can('create', Asset::class)
|
||||
|| $user->can('checkout', Asset::class)
|
||||
|| $user->can('checkin', Asset::class)
|
||||
|| $user->can('audit', Asset::class)
|
||||
|| $user->can('update', License::class)
|
||||
|| $user->can('create', License::class)
|
||||
|| $user->can('update', Component::class)
|
||||
|| $user->can('create', Component::class)
|
||||
|| $user->can('update', Consumable::class)
|
||||
|| $user->can('create', Consumable::class)
|
||||
|| $user->can('update', Accessory::class)
|
||||
|| $user->can('create', Accessory::class)
|
||||
|| $user->can('update', User::class)
|
||||
|| $user->can('create', User::class);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"erusev/parsedown": "^1.7",
|
||||
"facade/ignition": "^2.10",
|
||||
"fideloper/proxy": "^4.3",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"fruitcake/laravel-cors": "^2.2",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"intervention/image": "^2.5",
|
||||
"javiereguiluz/easyslugger": "^1.0",
|
||||
|
|
1542
composer.lock
generated
1542
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -250,6 +250,7 @@ return [
|
|||
|
||||
'enable_csp' => env('ENABLE_CSP', false),
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Require SAML Login
|
||||
|
@ -265,7 +266,6 @@ return [
|
|||
|
||||
'require_saml' => env('REQUIRE_SAML', false),
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Demo Mode Lockdown
|
||||
|
@ -420,4 +420,15 @@ return [
|
|||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Throttling
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the number of API requests permitted per minute
|
||||
|
|
||||
*/
|
||||
|
||||
'api_throttle_per_minute' => env('API_THROTTLE_PER_MINUTE', 120),
|
||||
|
||||
];
|
||||
|
|
|
@ -1,48 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------
|
||||
* THIS IS $allowed_origins code IS NOT PART OF THE ORIGINAL CORS PACKAGE.
|
||||
* IT IS A MODIFICATION BY SNIPE-IT TO ALLOW ADDING ALLOWED ORIGINS VIA THE ENV.
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Since we don't really want people editing config files (lest they get
|
||||
* overwritten later), this enables the person managing the Snipe-IT
|
||||
* installation to modify these values without modifying the code.
|
||||
*
|
||||
* If APP_CORS_ALLOWED_ORIGINS is not set in the .env (for example if no one added it
|
||||
* after an upgrade from a previous version that didn't include it in the .env.example) or is null,
|
||||
* set it to * to allow all. If there is a value, either a single url or a comma-delimited
|
||||
* list of urls, explode that out into an array to whitelist just those urls.
|
||||
*/
|
||||
|
||||
$allowed_origins = env('CORS_ALLOWED_ORIGINS') !== null ?
|
||||
explode(',', env('CORS_ALLOWED_ORIGINS')) : [];
|
||||
|
||||
/**
|
||||
* Original Laravel CORS package config file modifications end here
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
return [
|
||||
|
||||
'supportsCredentials' => false,
|
||||
|
||||
'allowedOrigins' => $allowed_origins,
|
||||
|
||||
'allowedHeaders' => ['*'],
|
||||
|
||||
'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
||||
|
||||
'maxAge' => 0,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||
| Laravel CORS
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your settings for cross-origin resource sharing
|
||||
| or "CORS". This determines what cross-origin operations may execute
|
||||
| in web browsers. You are free to adjust these settings as needed.
|
||||
|
|
||||
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
|
||||
| to accept any value.
|
||||
|
|
||||
*/
|
||||
|
||||
|
||||
'supports_credentials' => false,
|
||||
'allowed_origins' => $allowed_origins,
|
||||
'allowed_origins_patterns' => [],
|
||||
'allowed_headers' => ['*'],
|
||||
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
||||
'exposed_headers' => [],
|
||||
'max_age' => 0,
|
||||
'paths' => ['api/*', 'sanctum/csrf-cookie'],
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => ['*'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => false,
|
||||
|
||||
];
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array (
|
||||
'app_version' => 'v6.0.0-RC3',
|
||||
'full_app_version' => 'v6.0.0-RC3 - build 6627-g2815e0d36',
|
||||
'build_version' => '6627',
|
||||
'app_version' => 'v6.0.0-RC-5',
|
||||
'full_app_version' => 'v6.0.0-RC-5 - build 6772-g7cbcd2d95',
|
||||
'build_version' => '6772',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g2815e0d36',
|
||||
'full_hash' => 'v6.0.0-RC3-6-g2815e0d36',
|
||||
'branch' => 'develop',
|
||||
'hash_version' => 'g7cbcd2d95',
|
||||
'full_hash' => 'v6.0.0-RC-5-19-g7cbcd2d95',
|
||||
'branch' => 'merges/master_down_to_develop_march_16',
|
||||
);
|
|
@ -8,7 +8,6 @@ use App\Models\Category;
|
|||
use App\Models\Location;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use App\Models\StatusLabel;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -39,15 +38,16 @@ class AssetFactory extends Factory
|
|||
{
|
||||
return [
|
||||
'name' => null,
|
||||
'rtd_location_id' => Location::factory()->create(),
|
||||
'rtd_location_id' => Location::all()->random()->id,
|
||||
'serial' => $this->faker->uuid,
|
||||
'status_id' => $this->faker->numberBetween(1,5),
|
||||
'status_id' => 1,
|
||||
'user_id' => 1,
|
||||
'asset_tag' => $this->faker->unixTime('now'),
|
||||
'notes' => 'Created by DB seeder',
|
||||
'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()),
|
||||
'purchase_cost' => $this->faker->randomFloat(2, '299.99', '2999.99'),
|
||||
'order_number' => $this->faker->numberBetween(1000000, 50000000),
|
||||
'supplier_id' => Supplier::all()->random()->id,
|
||||
'requestable' => $this->faker->boolean(),
|
||||
'assigned_to' => null,
|
||||
'assigned_type' => null,
|
||||
|
|
|
@ -13,20 +13,22 @@ class CreateCheckoutAcceptancesTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('checkout_acceptances', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
if (!Schema::hasTable('checkout_acceptances')) {
|
||||
Schema::create('checkout_acceptances', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
|
||||
$table->morphs('checkoutable');
|
||||
$table->integer('assigned_to_id')->nullable();
|
||||
$table->morphs('checkoutable');
|
||||
$table->integer('assigned_to_id')->nullable();
|
||||
|
||||
$table->string('signature_filename')->nullable();
|
||||
$table->string('signature_filename')->nullable();
|
||||
|
||||
$table->timestamp('accepted_at')->nullable();
|
||||
$table->timestamp('declined_at')->nullable();
|
||||
$table->timestamp('accepted_at')->nullable();
|
||||
$table->timestamp('declined_at')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,6 +38,8 @@ class CreateCheckoutAcceptancesTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('checkout_acceptances');
|
||||
if (Schema::hasTable('checkout_acceptances')) {
|
||||
Schema::dropIfExists('checkout_acceptances');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
|
||||
class AddKitsLicensesTable extends Migration
|
||||
{
|
||||
|
@ -12,15 +14,16 @@ class AddKitsLicensesTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::create('kits_licenses', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('license_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
if (!Schema::hasTable('kits_licenses')) {
|
||||
Schema::create('kits_licenses', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('license_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
|
@ -29,7 +32,9 @@ class AddKitsLicensesTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('kits_licenses');
|
||||
}
|
||||
if (Schema::hasTable('kits_licenses')) {
|
||||
Schema::drop('kits_licenses');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddKitsTable extends Migration
|
||||
{
|
||||
|
@ -12,14 +13,16 @@ class AddKitsTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::create('kits', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->nullable()->default(null);
|
||||
$table->timestamps();
|
||||
$table->engine = 'InnoDB';
|
||||
});
|
||||
}
|
||||
if (!Schema::hasTable('kits')) {
|
||||
Schema::create('kits', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->nullable()->default(null);
|
||||
$table->timestamps();
|
||||
$table->engine = 'InnoDB';
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
|
@ -28,7 +31,9 @@ class AddKitsTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('kits');
|
||||
}
|
||||
if (Schema::hasTable('kits')) {
|
||||
Schema::drop('kits');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
|
||||
class AddKitsModelsTable extends Migration
|
||||
{
|
||||
|
@ -12,15 +14,16 @@ class AddKitsModelsTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::create('kits_models', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('model_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
if (!Schema::hasTable('kits_models')) {
|
||||
Schema::create('kits_models', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('model_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
|
@ -29,7 +32,8 @@ class AddKitsModelsTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('kits_models');
|
||||
if (Schema::hasTable('kits_models')) {
|
||||
Schema::drop('kits_models');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddKitsConsumablesTable extends Migration
|
||||
{
|
||||
|
@ -13,14 +13,15 @@ class AddKitsConsumablesTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::create('kits_consumables', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('consumable_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
if (!Schema::hasTable('kits_consumables')) {
|
||||
Schema::create('kits_consumables', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('consumable_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,8 @@ class AddKitsConsumablesTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('kits_consumables');
|
||||
if (Schema::hasTable('kits_consumables')) {
|
||||
Schema::drop('kits_consumables');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddKitsAccessoriesTable extends Migration
|
||||
{
|
||||
|
@ -13,14 +13,15 @@ class AddKitsAccessoriesTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::create('kits_accessories', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('accessory_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
if (!Schema::hasTable('kits_accessories')) {
|
||||
Schema::create('kits_accessories', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('kit_id')->nullable()->default(null);
|
||||
$table->integer('accessory_id')->nullable()->default(null);
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,8 @@ class AddKitsAccessoriesTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('kits_accessories');
|
||||
if (Schema::hasTable('kits_accessories')) {
|
||||
Schema::drop('kits_accessories');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddNotesToAccessories extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accessories', function (Blueprint $table) {
|
||||
$table->text("notes")->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accessories', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('accessories', 'notes')) {
|
||||
$table->dropColumn('notes');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddNotesToComponents extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('components', function (Blueprint $table) {
|
||||
$table->text("notes")->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('components', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('components', 'notes')) {
|
||||
$table->dropColumn('notes');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddNotesToConsumables extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('consumables', function (Blueprint $table) {
|
||||
$table->text("notes")->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('consumables', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('consumables', 'notes')) {
|
||||
$table->dropColumn('notes');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
34
database/migrations/2022_03_04_080836_add_remote_to_user.php
Normal file
34
database/migrations/2022_03_04_080836_add_remote_to_user.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddRemoteToUser extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('remote')->nullable()->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('users', 'remote')) {
|
||||
$table->dropColumn('remote');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -15662,7 +15662,7 @@
|
|||
"jquery": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||
"integrity": "sha1-xyoJ8Vwb3OFC9J2/EXC9+K2sJHA="
|
||||
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
||||
},
|
||||
"jquery-form-validator": {
|
||||
"version": "2.3.79",
|
||||
|
|
BIN
public/css/dist/skins/skin-black-dark.css
vendored
BIN
public/css/dist/skins/skin-black-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.css
vendored
BIN
public/css/dist/skins/skin-black.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.min.css
vendored
BIN
public/css/dist/skins/skin-black.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.css
vendored
BIN
public/css/dist/skins/skin-blue.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.min.css
vendored
BIN
public/css/dist/skins/skin-blue.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.css
vendored
BIN
public/css/dist/skins/skin-green-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.css
vendored
BIN
public/css/dist/skins/skin-green.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.min.css
vendored
BIN
public/css/dist/skins/skin-green.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.css
vendored
BIN
public/css/dist/skins/skin-orange.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.min.css
vendored
BIN
public/css/dist/skins/skin-orange.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.css
vendored
BIN
public/css/dist/skins/skin-purple.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.min.css
vendored
BIN
public/css/dist/skins/skin-purple.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.css
vendored
BIN
public/css/dist/skins/skin-red-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.css
vendored
BIN
public/css/dist/skins/skin-red.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.min.css
vendored
BIN
public/css/dist/skins/skin-red.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow.css
vendored
BIN
public/css/dist/skins/skin-yellow.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow.min.css
vendored
BIN
public/css/dist/skins/skin-yellow.min.css
vendored
Binary file not shown.
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=9056d096cc74e5a2e704",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=83e39e254b7f9035eddc",
|
||||
"/js/build/app.js": "/js/build/app.js?id=31d01a74b3501e01b608",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f1cc6901a7c5175c93ae",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=4fc3a0e0a16964643e70",
|
||||
"/css/build/app.css": "/css/build/app.css?id=a7cd7ad6e0e053ccf443",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=65ca7a34198fa16ba846",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=83271cb3576583918804",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d81888449b72ecd8dd24",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=04b51f791329b22290c3",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=8b631c0444622bf0bfd3",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=a663ec2c7842a3507df9",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=32c66312057953f69651",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=0a901949068bc7ce7f80",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=523144d33c7706098ed2",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=fafb61603f6db2f250df",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=ec96c42439cdeb022133",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=6e35fb4cb2f1063b3047",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=efda2335fa5243175850",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=2e5767d79e6c57bfbc4b",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=cc5e0b94b43961af9a42",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=6c99e33cd4d0c0f05a2d",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=55bdbfdce0c688dafd1c",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=98a647be9f296e72de5e",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=28831094cc2c076a9655",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=a4fa947ea4ea5cde9a93",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=4a9f983f15377677fc88",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=73257eccef2368713bd9",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=9dda5d1f907fef08fe97",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=55b303cf739c4c07a5a4",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=8184148eee63ebe2f13c",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=6a9d0ac448c28b88e5d6",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=c24716a423d375902723",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=77bd8e92475ecddc9c62",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=2ee76a9bb4100b60febc",
|
||||
"/css/blue.png": "/css/blue.png?id=e83a6c29e04fe851f212",
|
||||
"/css/blue@2x.png": "/css/blue@2x.png?id=51135dd4d24f88f5de0b",
|
||||
|
@ -26,20 +26,20 @@
|
|||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=810d7e520c3057ee500e",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=651427cc4b45d8e68d0c",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=675a086b82536dd212f0",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=ecc6ddb58fdd326a8a6d",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=efda2335fa5243175850",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=6e35fb4cb2f1063b3047",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=ec96c42439cdeb022133",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=fafb61603f6db2f250df",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=83e39e254b7f9035eddc",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=04b51f791329b22290c3",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=a663ec2c7842a3507df9",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=8b631c0444622bf0bfd3",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=c24716a423d375902723",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=523144d33c7706098ed2",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=0a901949068bc7ce7f80",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=32c66312057953f69651",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=83271cb3576583918804",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=d81888449b72ecd8dd24",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=575586844f1b9b67fc85",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=8184148eee63ebe2f13c",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=55b303cf739c4c07a5a4",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=9dda5d1f907fef08fe97",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=73257eccef2368713bd9",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f1cc6901a7c5175c93ae",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=6c99e33cd4d0c0f05a2d",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=98a647be9f296e72de5e",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=55bdbfdce0c688dafd1c",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=77bd8e92475ecddc9c62",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=4a9f983f15377677fc88",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=a4fa947ea4ea5cde9a93",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=28831094cc2c076a9655",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=2e5767d79e6c57bfbc4b",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=cc5e0b94b43961af9a42",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=6a9d0ac448c28b88e5d6"
|
||||
}
|
||||
|
|
|
@ -130,7 +130,6 @@
|
|||
{id: 'location', text: 'Location' },
|
||||
{id: 'maintained', text: 'Maintained' },
|
||||
{id: 'manufacturer', text: 'Manufacturer' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'order_number', text: 'Order Number' },
|
||||
{id: 'purchase_cost', text: 'Purchase Cost' },
|
||||
{id: 'purchase_date', text: 'Purchase Date' },
|
||||
|
@ -143,14 +142,19 @@
|
|||
],
|
||||
accessories:[
|
||||
{id: 'model_number', text: 'Model Number'},
|
||||
{id: 'notes', text: 'Notes' },
|
||||
],
|
||||
assets: [
|
||||
{id: 'asset_tag', text: 'Asset Tag' },
|
||||
{id: 'asset_model', text: 'Model Name' },
|
||||
{id: 'asset_notes', text: 'Asset Notes' },
|
||||
{id: 'model_notes', text: 'Model Notes' },
|
||||
{id: 'checkout_class', text: 'Checkout Type' },
|
||||
{id: 'checkout_location', text: 'Checkout Location' },
|
||||
{id: 'image', text: 'Image Filename' },
|
||||
{id: 'model_number', text: 'Model Number' },
|
||||
{id: 'asset_notes', text: 'Asset Notes' },
|
||||
{id: 'model_notes', text: 'Model Notes' },
|
||||
{id: 'full_name', text: 'Full Name' },
|
||||
{id: 'status', text: 'Status' },
|
||||
{id: 'warranty_months', text: 'Warranty Months' },
|
||||
|
@ -161,13 +165,16 @@
|
|||
{id: 'item_no', text: "Item Number"},
|
||||
{id: 'model_number', text: "Model Number"},
|
||||
{id: 'min_amt', text: "Minimum Quantity"},
|
||||
{id: 'notes', text: 'Notes' },
|
||||
],
|
||||
licenses: [
|
||||
{id: 'asset_tag', text: 'Assigned To Asset'},
|
||||
{id: 'expiration_date', text: 'Expiration Date' },
|
||||
{id: 'full_name', text: 'Full Name' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'license_email', text: 'Licensed To Email' },
|
||||
{id: 'license_name', text: 'Licensed To Name' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'purchase_order', text: 'Purchase Order' },
|
||||
{id: 'reassignable', text: 'Reassignable' },
|
||||
{id: 'seats', text: 'Seats' },
|
||||
|
@ -179,7 +186,9 @@
|
|||
{id: 'last_name', text: 'Last Name' },
|
||||
{id: 'phone_number', text: 'Phone Number' },
|
||||
{id: 'manager_first_name', text: 'Manager First Name' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'manager_last_name', text: 'Manager Last Name' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'activated', text: 'Activated' },
|
||||
{id: 'address', text: 'Address' },
|
||||
{id: 'city', text: 'City' },
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
$(function () {
|
||||
|
||||
|
||||
var baseUrl = $('meta[name="baseUrl"]').attr('content');
|
||||
//handle modal-add-interstitial calls
|
||||
var model, select, refreshSelector;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
color: #fff;
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
color: #333;
|
||||
&:hover {
|
||||
background: darken(@black, 5%);
|
||||
color: #fff;
|
||||
|
@ -36,6 +36,11 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
|
||||
background-color: var(--back-sub);
|
||||
}
|
||||
}
|
||||
//Logo
|
||||
|
||||
li.user-header {
|
||||
|
@ -245,10 +250,6 @@ body {
|
|||
#details>div>div>div>table {
|
||||
background-color: transparent;
|
||||
}
|
||||
.dropdown-menu, .dropdown-menu>li>a {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main) !important;
|
||||
}
|
||||
.dropdown-menu>li>a:hover {
|
||||
background-color: var(--back-main);
|
||||
color: var(--nav-link);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
color: #333;
|
||||
&:hover {
|
||||
background: darken(@black, 5%);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue