diff --git a/.env.example b/.env.example
index 5ad08a4fa1..b85385d559 100644
--- a/.env.example
+++ b/.env.example
@@ -77,6 +77,7 @@ ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
CORS_ALLOWED_ORIGINS=null
+ENABLE_HSTS=false
# --------------------------------------------
# OPTIONAL: CACHE SETTINGS
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000000..7ef56a0d5f
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,40 @@
+# Description
+
+Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
+
+Fixes # (issue)
+
+## Type of change
+
+Please delete options that are not relevant.
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] This change requires a documentation update
+
+# How Has This Been Tested?
+
+Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
+
+- [ ] Test A
+- [ ] Test B
+
+**Test Configuration**:
+* PHP version:
+* MySQL version
+* Webserver version
+* OS version
+
+
+# Checklist:
+
+- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
+- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
+- [ ] My code follows the style guidelines of this project
+- [ ] I have performed a self-review of my own code
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have made corresponding changes to the documentation
+- [ ] My changes generate no new warnings
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
diff --git a/README.md b/README.md
index d9b7db5a1c..2e85a4ed60 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeitapp) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
+[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
@@ -114,6 +115,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [ Martin Stub](http://martinstub.dk) [π](#translation-stubben "Translation") | [ Meyer Flavio](https://github.com/meyerf99) [π](#translation-meyerf99 "Translation") | [ Micael Rodrigues](https://github.com/MicaelRodrigues) [π](#translation-MicaelRodrigues "Translation") | [ Mikael Rasmussen](http://rubixy.com/) [π](#translation-mikaelssen "Translation") | [ IxFail](https://github.com/IxFail) [π](#translation-IxFail "Translation") | [ Mohammed Fota](http://www.mohammedfota.com) [π](#translation-MohammedFota "Translation") | [ Moayad Alserihi](https://github.com/omego) [π](#translation-omego "Translation") |
| [ saymd](https://github.com/saymd) [π](#translation-saymd "Translation") | [ Patrik Larsson](https://nordsken.se) [π](#translation-pooot "Translation") | [ drcryo](https://github.com/drcryo) [π](#translation-drcryo "Translation") | [ pawel1615](https://github.com/pawel1615) [π](#translation-pawel1615 "Translation") | [ bodrovics](https://github.com/bodrovics) [π](#translation-bodrovics "Translation") | [ priatna](https://github.com/priatna) [π](#translation-priatna "Translation") | [ Fan Jiang](https://amayume.net) [π](#translation-ProfFan "Translation") |
| [ ragnarcx](https://github.com/ragnarcx) [π](#translation-ragnarcx "Translation") | [ Rein van Haaren](http://www.reinvanhaaren.nl/) [π](#translation-reinvanhaaren "Translation") | [ Teguh Dwicaksana](http://dheche.songolimo.net) [π](#translation-dheche "Translation") | [ fraccie](https://github.com/FRaccie) [π](#translation-FRaccie "Translation") | [ vinzruzell](https://github.com/vinzruzell) [π](#translation-vinzruzell "Translation") | [ Kevin Austin](http://kevinaustin.com) [π](#translation-vipsystem "Translation") | [ Wira Sandy](http://azuraweb.xyz) [π](#translation-wira-sandy "Translation") |
+| [ ΠΠ»ΡΡ](https://github.com/GrayHoax) [π](#translation-GrayHoax "Translation") | [ GodUseVPN](https://github.com/godusevpn) [π](#translation-godusevpn "Translation") | [ ε¨ε¨](https://github.com/EngrZhou) [π](#translation-EngrZhou "Translation") | [ Sam](https://github.com/takuy) [π»](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [ Azerothian](https://www.illisian.com.au) [π»](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [ Tim Farmer](https://github.com/timothyfarmer) [π»](https://github.com/snipe/snipe-it/commits?author=timothyfarmer "Code") | [ MariΓ‘n Skrip](https://github.com/mskrip) [π»](https://github.com/snipe/snipe-it/commits?author=mskrip "Code") |
+| [ Godfrey Martinez](https://github.com/Godmartinz) [π»](https://github.com/snipe/snipe-it/commits?author=Godmartinz "Code") | [ bigtreeEdo](https://github.com/bigtreeEdo) [π»](https://github.com/snipe/snipe-it/commits?author=bigtreeEdo "Code") | [ Colin McNeil](https://colinmcneil.me/) [π»](https://github.com/snipe/snipe-it/commits?author=ColinMcNeil "Code") | [ JoKneeMo](https://github.com/JoKneeMo) [π»](https://github.com/snipe/snipe-it/commits?author=JoKneeMo "Code") | [ Joshi](http://www.redbridge.se) [π»](https://github.com/snipe/snipe-it/commits?author=joshi-redbridge "Code") | [ Anthony Burns](https://github.com/anthonypburns) [π»](https://github.com/snipe/snipe-it/commits?author=anthonypburns "Code") | [ Alexander Chibrikin](http://phpprofi.ru/) [π»](https://github.com/snipe/snipe-it/commits?author=alek13 "Code") |
| [ ΠΠ»ΡΡ](https://github.com/GrayHoax) [π](#translation-GrayHoax "Translation") | [ GodUseVPN](https://github.com/godusevpn) [π](#translation-godusevpn "Translation") | [ ε¨ε¨](https://github.com/EngrZhou) [π](#translation-EngrZhou "Translation") | [ Sam](https://github.com/takuy) [π»](https://github.com/snipe/snipe-it/commits?author=takuy "Code") | [ Azerothian](https://www.illisian.com.au) [π»](https://github.com/snipe/snipe-it/commits?author=Azerothian "Code") | [ Wes Hulette](http://macfoo.wordpress.com/) [π»](https://github.com/snipe/snipe-it/commits?author=jwhulette "Code") | [ patrict](https://github.com/patrict) [π»](https://github.com/snipe/snipe-it/commits?author=patrict "Code") |
| [ Dmitriy Minaev](https://github.com/VELIKII-DIVAN) [π»](https://github.com/snipe/snipe-it/commits?author=VELIKII-DIVAN "Code") | [ liquidhorse](https://github.com/liquidhorse) [π»](https://github.com/snipe/snipe-it/commits?author=liquidhorse "Code") | [ Jordi Boggiano](https://seld.be/) [π»](https://github.com/snipe/snipe-it/commits?author=Seldaek "Code") | [ Ivan Nieto](https://github.com/inietov) [π»](https://github.com/snipe/snipe-it/commits?author=inietov "Code") | [ Ben RUBSON](https://github.com/benrubson) [π»](https://github.com/snipe/snipe-it/commits?author=benrubson "Code") | [ NMathar](https://github.com/NMathar) [π»](https://github.com/snipe/snipe-it/commits?author=NMathar "Code") | [ Steffen](https://github.com/smb) [π»](https://github.com/snipe/snipe-it/commits?author=smb "Code") |
| [ Sxderp](https://github.com/Sxderp) [π»](https://github.com/snipe/snipe-it/commits?author=Sxderp "Code") | [ fanta8897](https://github.com/fanta8897) [π»](https://github.com/snipe/snipe-it/commits?author=fanta8897 "Code") | [ Andrey Bolonin](https://andreybolonin.com/phpconsulting/) [π»](https://github.com/snipe/snipe-it/commits?author=andreybolonin "Code") | [ shinayoshi](http://www.shinayoshi.net/) [π»](https://github.com/snipe/snipe-it/commits?author=shinayoshi "Code") | [ Hubert](https://github.com/reuser) [π»](https://github.com/snipe/snipe-it/commits?author=reuser "Code") | [ KeenRivals](https://brashear.me) [π»](https://github.com/snipe/snipe-it/commits?author=KeenRivals "Code") | [ omyno](https://github.com/omyno) [π»](https://github.com/snipe/snipe-it/commits?author=omyno "Code") |
diff --git a/app/Console/Commands/CheckinLicensesFromAllUsers.php b/app/Console/Commands/CheckinLicensesFromAllUsers.php
new file mode 100644
index 0000000000..bcd8583eaf
--- /dev/null
+++ b/app/Console/Commands/CheckinLicensesFromAllUsers.php
@@ -0,0 +1,95 @@
+option('license_id');
+ $notify = $this->option('notify');
+
+ if (!$license_id) {
+ $this->error('ERROR: License ID is required.');
+ return false;
+ }
+
+
+ if (!$license = License::where('id','=',$license_id)->first()) {
+ $this->error('Invalid license ID');
+ return false;
+ }
+
+ $this->info('Checking in ALL seats for '.$license->name);
+
+
+ $licenseSeats = LicenseSeat::where('license_id', '=', $license_id)
+ ->whereNotNull('assigned_to')
+ ->with('user')
+ ->get();
+
+ $this->info(' There are ' .$licenseSeats->count(). ' seats checked out: ');
+
+ if (!$notify) {
+ $this->info('No mail will be sent.');
+ }
+
+ foreach ($licenseSeats as $seat) {
+ $this->info($seat->user->username .' has a license seat for '.$license->name);
+ $seat->assigned_to = null;
+
+ if ($seat->save()) {
+
+ // Override the email address so we don't notify on checkin
+ if (!$notify) {
+ $seat->user->email = null;
+ }
+
+ // Log the checkin
+ $seat->logCheckin($seat->user, 'Checked in via cli tool');
+ }
+
+
+
+
+ }
+
+
+ }
+}
diff --git a/app/Console/Commands/CheckoutLicenseToAllUsers.php b/app/Console/Commands/CheckoutLicenseToAllUsers.php
new file mode 100644
index 0000000000..0855e5d084
--- /dev/null
+++ b/app/Console/Commands/CheckoutLicenseToAllUsers.php
@@ -0,0 +1,112 @@
+option('license_id');
+ $notify = $this->option('notify');
+
+ if (!$license_id) {
+ $this->error('ERROR: License ID is required.');
+ return false;
+ }
+
+
+ if (!$license = License::where('id','=',$license_id)->with('assignedusers')->first()) {
+ $this->error('Invalid license ID');
+ return false;
+ }
+
+ $users = User::whereNull('deleted_at')->with('licenses')->get();
+
+ if ($users->count() > $license->getAvailSeatsCountAttribute()) {
+ $this->info('You do not have enough free seats to complete this task, so we will check out as many as we can. ');
+ }
+
+ $this->info('Checking out '.$users->count().' of '.$license->getAvailSeatsCountAttribute().' seats for '.$license->name);
+
+ if (!$notify) {
+ $this->info('No mail will be sent.');
+ }
+
+ foreach ($users as $user) {
+
+ // Check to make sure this user doesn't already have this license checked out
+ // to them
+
+ if ($user->licenses->where('id', '=', $license_id)->count()) {
+ $this->info($user->username .' already has this license checked out to them. Skipping... ');
+ continue;
+ }
+
+
+ // If the license is valid, check that there is an available seat
+ if ($license->availCount()->count() < 1) {
+ $this->error('ERROR: No available seats');
+ return false;
+ }
+
+ $this->info($license->availCount()->count().' seats left');
+ // Get the seat ID
+ $licenseSeat = $license->freeSeat();
+
+
+ // Update the seat with checkout info,
+ $licenseSeat->assigned_to = $user->id;
+ if ($licenseSeat->save()) {
+
+ // Temporarily null the user's email address so we don't send mail if we're not supposed to
+ if (!$notify) {
+ $user->email = null;
+ }
+
+ // Log the checkout
+ $licenseSeat->logCheckout('Checked out via cli tool', $user);
+ $this->info('License '.$license_id.' seat '.$licenseSeat->id.' checked out to '.$user->username);
+ }
+
+ }
+
+
+
+ }
+}
diff --git a/app/Console/Commands/MergeUsersByUsername.php b/app/Console/Commands/MergeUsersByUsername.php
new file mode 100644
index 0000000000..ded5d1986f
--- /dev/null
+++ b/app/Console/Commands/MergeUsersByUsername.php
@@ -0,0 +1,109 @@
+whereNull('deleted_at')->get();
+
+ foreach ($users as $user) {
+ $parts = explode("@", $user->username);
+ $bad_users = User::where('username', '=', $parts[0])->whereNull('deleted_at')->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations')->get();
+
+ foreach ($bad_users as $bad_user) {
+ $this->info($bad_user->username.' ('.$bad_user->id.') will be merged into '.$user->username.' ('.$user->id.') ');
+
+ // Walk the list of assets
+ foreach ($bad_user->assets as $asset) {
+ $this->info( 'Updating asset '.$asset->asset_tag.' '.$asset->id.' to user '.$user->id);
+ $asset->assigned_to = $user->id;
+ $asset->save();
+ }
+
+ // Walk the list of licenses
+ foreach ($bad_user->licenses as $license) {
+ $this->info( 'Updating license '.$license->name.' '.$license->id.' to user '.$user->id);
+ $bad_user->licenses()->updateExistingPivot($license->id, ['assigned_to' => $user->id]);
+ }
+
+ // Walk the list of consumables
+ foreach ($bad_user->consumables as $consumable) {
+ $this->info( 'Updating consumable '.$consumable->id.' to user '.$user->id);
+ $bad_user->consumables()->updateExistingPivot($consumable->id, ['assigned_to' => $user->id]);
+ }
+
+ // Walk the list of accessories
+ foreach ($bad_user->accessories as $accessory) {
+ $this->info( 'Updating accessory '.$accessory->id.' to user '.$user->id);
+ $bad_user->accessories()->updateExistingPivot($accessory->id, ['assigned_to' => $user->id]);
+ }
+
+ // Walk the list of logs
+ foreach ($bad_user->userlog as $log) {
+ $this->info( 'Updating action log record '.$log->id.' to user '.$user->id);
+ $log->target_id = $user->id;
+ $log->save();
+ }
+
+ // Update any manager IDs
+ $this->info( 'Updating managed user records to user '.$user->id);
+ User::where('manager_id', '=', $bad_user->id)->update(['manager_id' => $user->id]);
+
+
+ // Update location manager IDs
+ foreach ($bad_user->managedLocations as $managedLocation) {
+ $this->info( 'Updating managed location record '.$managedLocation->name.' to manager '.$user->id);
+ $managedLocation->manager_id = $user->id;
+ $managedLocation->save();
+ }
+
+ // Mark the user as deleted
+ $this->info( 'Marking the user as deleted');
+ $bad_user->deleted_at = Carbon::now()->timestamp;
+ $bad_user->save();
+
+
+ }
+
+ }
+
+
+ }
+}
diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php
index 5585f3a695..4c4e2a8a92 100644
--- a/app/Http/Controllers/Api/AssetsController.php
+++ b/app/Http/Controllers/Api/AssetsController.php
@@ -457,6 +457,7 @@ class AssetsController extends Controller
$asset->supplier_id = $request->get('supplier_id', 0);
$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);
if ($request->has('image_source') && $request->input('image_source') != "") {
$saved_image_path = Helper::processUploadedImage(
diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php
index a3b785333f..b1d953b975 100644
--- a/app/Http/Controllers/Api/ConsumablesController.php
+++ b/app/Http/Controllers/Api/ConsumablesController.php
@@ -8,7 +8,9 @@ use App\Http\Transformers\ConsumablesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Company;
use App\Models\Consumable;
-use Illuminate\Http\Request;
+use App\Models\User;
+use App\Http\Transformers\ConsumablesTransformer;
+use App\Helpers\Helper;
class ConsumablesController extends Controller
{
@@ -158,7 +160,7 @@ class ConsumablesController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.delete.success')));
}
- /**
+ /**
* Returns a JSON response containing details on the users associated with this consumable.
*
* @author [A. Gianotto] []
@@ -198,6 +200,57 @@ class ConsumablesController extends Controller
return $data;
}
+ /**
+ * Checkout a consumable
+ *
+ * @author [A. Gutierrez] []
+ * @param int $id
+ * @since [v4.9.5]
+ * @return JsonResponse
+ */
+ public function checkout(Request $request, $id)
+ {
+ // Check if the consumable exists
+ if (is_null($consumable = Consumable::find($id))) {
+ return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.does_not_exist')));
+ }
+
+ $this->authorize('checkout', $consumable);
+
+ if ($consumable->qty > 0) {
+
+ // Check if the user exists
+ $assigned_to = $request->input('assigned_to');
+ if (is_null($user = User::find($assigned_to))) {
+ // Return error message
+ return response()->json(Helper::formatStandardApiResponse('error', null, 'No user found'));
+ }
+
+ // Update the consumable data
+ $consumable->assigned_to = e($assigned_to);
+
+ $consumable->users()->attach($consumable->id, [
+ 'consumable_id' => $consumable->id,
+ 'user_id' => $user->id,
+ 'assigned_to' => $assigned_to
+ ]);
+
+ // Log checkout event
+ $logaction = $consumable->logCheckout(e($request->input('note')), $user);
+ $data['log_id'] = $logaction->id;
+ $data['eula'] = $consumable->getEula();
+ $data['first_name'] = $user->first_name;
+ $data['item_name'] = $consumable->name;
+ $data['checkout_date'] = $logaction->created_at;
+ $data['note'] = $logaction->note;
+ $data['require_acceptance'] = $consumable->requireAcceptance();
+
+ return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
+ }
+
+ return response()->json(Helper::formatStandardApiResponse('error', null, 'No consumables remaining'));
+ }
+
/**
* Gets a paginated collection for the select2 menus
*
diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php
index 5f747726c9..a130eaa1db 100644
--- a/app/Http/Controllers/Api/UsersController.php
+++ b/app/Http/Controllers/Api/UsersController.php
@@ -78,6 +78,14 @@ class UsersController extends Controller
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
+ if ($request->filled('email')) {
+ $users = $users->where('users.email', '=', $request->input('email'));
+ }
+
+ if ($request->filled('username')) {
+ $users = $users->where('users.username', '=', $request->input('username'));
+ }
+
if ($request->filled('group_id')) {
$users = $users->ByGroup($request->get('group_id'));
}
diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php
index 2f25deab92..3ed9940118 100644
--- a/app/Http/Controllers/Auth/LoginController.php
+++ b/app/Http/Controllers/Auth/LoginController.php
@@ -385,6 +385,7 @@ class LoginController extends Controller
$request->session()->regenerate(true);
+ $request->session()->regenerate(true);
Auth::logout();
if (!empty($sloRedirectUrl)) {
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index da3c5092b9..0045496791 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -17,15 +17,12 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
- \App\Http\Middleware\FrameGuard::class,
- \App\Http\Middleware\XssProtectHeader::class,
- \App\Http\Middleware\ReferrerPolicyHeader::class,
- \App\Http\Middleware\ContentSecurityPolicyHeader::class,
- \App\Http\Middleware\NosniffGuard::class,
\Fideloper\Proxy\TrustProxies::class,
\App\Http\Middleware\CheckForSetup::class,
\App\Http\Middleware\CheckForDebug::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+ \App\Http\Middleware\SecurityHeaders::class,
+
];
/**
diff --git a/app/Http/Middleware/ContentSecurityPolicyHeader.php b/app/Http/Middleware/ContentSecurityPolicyHeader.php
deleted file mode 100644
index 45c87a59e3..0000000000
--- a/app/Http/Middleware/ContentSecurityPolicyHeader.php
+++ /dev/null
@@ -1,35 +0,0 @@
-headers->set('Content-Security-Policy', $policy);
- return $response;
- }
-}
diff --git a/app/Http/Middleware/FrameGuard.php b/app/Http/Middleware/FrameGuard.php
deleted file mode 100644
index beb19f20f1..0000000000
--- a/app/Http/Middleware/FrameGuard.php
+++ /dev/null
@@ -1,24 +0,0 @@
-headers->set('X-Frame-Options', 'SAMEORIGIN', false);
- }
- return $response;
-
- }
-}
diff --git a/app/Http/Middleware/NosniffGuard.php b/app/Http/Middleware/NosniffGuard.php
deleted file mode 100644
index 295f5e75af..0000000000
--- a/app/Http/Middleware/NosniffGuard.php
+++ /dev/null
@@ -1,21 +0,0 @@
-headers->set('X-Content-Type-Options', 'nosniff', false);
- return $response;
- }
-}
diff --git a/app/Http/Middleware/ReferrerPolicyHeader.php b/app/Http/Middleware/ReferrerPolicyHeader.php
deleted file mode 100644
index 430ce45af3..0000000000
--- a/app/Http/Middleware/ReferrerPolicyHeader.php
+++ /dev/null
@@ -1,21 +0,0 @@
-headers->set('Referrer-Policy', config('app.referrer_policy'));
- return $response;
- }
-}
diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php
new file mode 100644
index 0000000000..ab88ec1b87
--- /dev/null
+++ b/app/Http/Middleware/SecurityHeaders.php
@@ -0,0 +1,122 @@
+removeUnwantedHeaders($this->unwantedHeaderList);
+ $response = $next($request);
+
+ $response->headers->set('X-Content-Type-Options', 'nosniff');
+ $response->headers->set('X-XSS-Protection', '1; mode=block');
+
+ // Ugh. Feature-Policy is dumb and clumsy and mostly irrelevant for Snipe-IT,
+ // since we don't provide any way to IFRAME anything in in the first place.
+ // There is currently no easy way to default ALL THE THINGS to 'none', but
+ // security audits will still ding you if you don't have this header, even
+ // though we don't allow IFRAMING in the first place.
+ //
+ // So for security and compliance sake, here we are. Sigh.
+ //
+ // See also:
+ // - https://developers.google.com/web/updates/2018/06/feature-policy
+ // - https://scotthelme.co.uk/a-new-security-header-feature-policy/
+ // - 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 = join(';', $feature_policy);
+ $response->headers->set('Feature-Policy', $feature_policy);
+
+
+
+ // Defaults to same-origin if REFERRER_POLICY is not set in the .env
+ $response->headers->set('Referrer-Policy', config('app.referrer_policy'));
+
+ // The .env var ALLOW_IFRAMING defaults to false (which disallows IFRAMING)
+ // if not present, but some unique cases require this to be enabled.
+ // For example, some IT depts have IFRAMED Snipe-IT into their IT portal
+ // for convenience so while it is normally disallowed, there is
+ // an override that exists.
+
+ if (config('app.allow_iframing') == false) {
+ $response->headers->set('X-Frame-Options', 'DENY');
+ }
+
+
+ // This defaults to false to maintain backwards compatibility for
+ // people who are not running Snipe-IT over TLS (shame, shame, shame!)
+ // Seriously though, please run Snipe-IT over TLS. Let's Encrypt is free.
+ // https://letsencrypt.org
+
+ if (config('app.enable_hsts') === true) {
+ $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
+ }
+
+ // We have to exclude debug mode here because debugbar pulls from a CDN or two
+ // and it will break things.
+
+ if ((config('app.debug')!='true') || (config('app.enable_csp')=='true')) {
+ $csp_policy[] = "default-src 'self'";
+ $csp_policy[] = "style-src 'self' 'unsafe-inline'";
+ $csp_policy[] = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
+ $csp_policy[] = "connect-src 'self'";
+ $csp_policy[] = "object-src 'none'";
+ $csp_policy[] = "font-src 'self' data:";
+ $csp_policy[] = "img-src 'self' data: gravatar.com maps.google.com maps.gstatic.com *.googleapis.com";
+ $csp_policy = join(';', $csp_policy);
+ $response->headers->set('Content-Security-Policy', $csp_policy);
+ }
+
+ return $response;
+ }
+
+ private function removeUnwantedHeaders($headerList)
+ {
+ foreach ($headerList as $header)
+ header_remove($header);
+ }
+}
diff --git a/app/Http/Middleware/XssProtectHeader.php b/app/Http/Middleware/XssProtectHeader.php
deleted file mode 100644
index 868d100f37..0000000000
--- a/app/Http/Middleware/XssProtectHeader.php
+++ /dev/null
@@ -1,22 +0,0 @@
-headers->set('X-XSS-Protection', $mode);
- return $response;
- }
-}
diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php
index d157b16ab0..6466c3b2d1 100644
--- a/app/Importer/Importer.php
+++ b/app/Importer/Importer.php
@@ -63,6 +63,10 @@ abstract class Importer
'full_name' => 'full name',
'email' => 'email',
'username' => 'username',
+ 'address' => 'address',
+ 'city' => 'city',
+ 'state' => 'state',
+ 'country' => 'country',
'jobtitle' => 'job title',
'employee_num' => 'employee number',
'phone_number' => 'phone number',
diff --git a/app/Importer/UserImporter.php b/app/Importer/UserImporter.php
index acbfab54f7..4fcf221cba 100644
--- a/app/Importer/UserImporter.php
+++ b/app/Importer/UserImporter.php
@@ -48,6 +48,10 @@ class UserImporter extends ItemImporter
$this->item['email'] = $this->findCsvMatch($row, 'email');
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
+ $this->item['address'] = $this->findCsvMatch($row, 'address');
+ $this->item['city'] = $this->findCsvMatch($row, 'city');
+ $this->item['state'] = $this->findCsvMatch($row, 'state');
+ $this->item['country'] = $this->findCsvMatch($row, 'country');
$this->item['activated'] = ($this->fetchHumanBoolean($this->findCsvMatch($row, 'activated')) == 1) ? '1' : 0;
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
$this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department'));
diff --git a/app/Importer/import_mappings.md b/app/Importer/import_mappings.md
index 0642241525..7899cf679a 100644
--- a/app/Importer/import_mappings.md
+++ b/app/Importer/import_mappings.md
@@ -7,13 +7,14 @@
| department_id | | User ? All |
| item name | item_name | All |
| image | image | Asset |
-| department_id | | User ? All |
+| email | | |
| expiration date | expiration_date | License |
| location | location | All |
| notes | notes | All |
| licensed to email | license_email | License |
| licensed to name | license_name | License |
| maintained | maintained | License |
+| manager_id | | User |
| manufacturer | manufacturer | All |
| model name | asset_model | Asset |
| model number | model_number | Asset |
@@ -34,4 +35,8 @@
| name | | |
| email | | |
| username | | |
+| address | address | User |
+| city | city | User |
+| state | state | User |
+| country | country | User |
diff --git a/app/Models/Asset.php b/app/Models/Asset.php
index 2a92c81a5a..9af62593d7 100644
--- a/app/Models/Asset.php
+++ b/app/Models/Asset.php
@@ -1094,7 +1094,7 @@ class Asset extends Depreciable
$interval = $settings->audit_warning_days ?? 0;
return $query->whereNotNull('assets.next_audit_date')
- ->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
+ ->whereRaw("DATE_SUB(".DB::getTablePrefix()."assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
->where('assets.archived', '=', 0)
->NotArchived();
}
diff --git a/app/Models/Company.php b/app/Models/Company.php
index c81655029e..e6bd3dd773 100644
--- a/app/Models/Company.php
+++ b/app/Models/Company.php
@@ -126,9 +126,12 @@ final class Company extends SnipeModel
} elseif (!static::isFullMultipleCompanySupportEnabled()) {
return true;
} else {
- $current_user_company_id = Auth::user()->company_id;
- $companyable_company_id = $companyable->company_id;
- return ($current_user_company_id == null || $current_user_company_id == $companyable_company_id || Auth::user()->isSuperUser());
+ if (Auth::user()) {
+ $current_user_company_id = Auth::user()->company_id;
+ $companyable_company_id = $companyable->company_id;
+ return ($current_user_company_id == null || $current_user_company_id == $companyable_company_id || Auth::user()->isSuperUser());
+ }
+
}
}
diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php
index 1bb05b9298..bff1ac2e10 100644
--- a/app/Models/Loggable.php
+++ b/app/Models/Loggable.php
@@ -28,8 +28,9 @@ trait Loggable
{
$log = new Actionlog;
$log = $this->determineLogItemType($log);
- if(Auth::user())
+ if (Auth::user()) {
$log->user_id = Auth::user()->id;
+ }
if (!isset($target)) {
throw new \Exception('All checkout logs require a target.');
@@ -113,14 +114,45 @@ trait Loggable
$log->location_id = null;
$log->note = $note;
- $log->action_date = $action_date;
-
- if (!$log->action_date) {
- $log->action_date = date('Y-m-d H:i:s');
+ if (Auth::user()) {
+ $log->user_id = Auth::user()->id;
}
- $log->user_id = Auth::user()->id;
+
$log->logaction('checkin from');
+ $params = [
+ 'target' => $target,
+ 'item' => $log->item,
+ 'admin' => $log->user,
+ 'note' => $note,
+ 'target_type' => $log->target_type,
+ 'settings' => $settings,
+ ];
+
+
+ $checkinClass = null;
+
+ if (method_exists($target, 'notify')) {
+ try {
+ $target->notify(new static::$checkinClass($params));
+ } catch (\Exception $e) {
+ \Log::debug($e);
+ }
+
+ }
+
+ // Send to the admin, if settings dictate
+ $recipient = new \App\Models\Recipients\AdminRecipient();
+
+ if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) {
+ try {
+ $recipient->notify(new static::$checkinClass($params));
+ } catch (\Exception $e) {
+ \Log::debug($e);
+ }
+
+ }
+
return $log;
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 109ad4f92d..ff886905fc 100755
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -331,7 +331,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
*/
public function userlog()
{
- return $this->hasMany('\App\Models\Actionlog', 'target_id')->orderBy('created_at', 'DESC')->withTrashed();
+ return $this->hasMany('\App\Models\Actionlog', 'target_id')->where('target_type', '=', 'App\Models\User')->orderBy('created_at', 'DESC')->withTrashed();
}
@@ -521,6 +521,18 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
} elseif ($format=='firstname') {
$username = str_slug($first_name);
}
+ elseif ($format=='firstinitial.lastname') {
+ $username = str_slug(substr($first_name, 0, 1). '.' . str_slug($last_name));
+ }
+ elseif ($format=='lastname_firstinitial') {
+ $username = str_slug($last_name).'_'.str_slug(substr($first_name, 0, 1));
+ }
+ elseif ($format=='firstnamelastname') {
+ $username = str_slug($first_name) . str_slug($last_name);
+ }
+ elseif ($format=='firstnamelastinitial') {
+ $username = str_slug(($first_name.substr($last_name, 0, 1)));
+ }
}
$user['first_name'] = $first_name;
diff --git a/app/Notifications/CheckinLicenseSeatNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php
index 07d5455b13..f9d4914c37 100644
--- a/app/Notifications/CheckinLicenseSeatNotification.php
+++ b/app/Notifications/CheckinLicenseSeatNotification.php
@@ -67,10 +67,18 @@ class CheckinLicenseSeatNotification extends Notification
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
- $fields = [
- 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
- 'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
- ];
+ if ($admin) {
+ $fields = [
+ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
+ 'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>',
+ ];
+ } else {
+ $fields = [
+ 'To' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
+ 'By' => 'CLI tool',
+ ];
+ }
+
diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php
index 4b791be0c1..5e009ebfa1 100644
--- a/app/Presenters/AssetPresenter.php
+++ b/app/Presenters/AssetPresenter.php
@@ -268,8 +268,11 @@ class AssetPresenter extends Presenter
"searchable" => true,
"sortable" => true,
"switchable" => true,
- "title" => ($field->field_encrypted=='1') ?' '.$field->name : $field->name,
- "formatter" => "customFieldsFormatter"
+ "title" => $field->name,
+ "formatter"=> 'customFieldsFormatter',
+ "escape" => true,
+ "class" => ($field->field_encrypted=='1') ? 'css-padlock' : '',
+ "visible" => true,
];
}
diff --git a/config/app.php b/config/app.php
index b4c21884b3..9f1f03129b 100755
--- a/config/app.php
+++ b/config/app.php
@@ -197,19 +197,33 @@ return [
/*
- |--------------------------------------------------------------------------
- | ALLOW I-FRAMING
- |--------------------------------------------------------------------------
- |
- | Normal users will never need to edit this. This option lets you run
- | Snipe-IT within an I-Frame, which is normally disabled by default for
- | security reasons, to prevent clickjacking. It should normally be set to false.
- |
- */
+ |--------------------------------------------------------------------------
+ | ALLOW I-FRAMING
+ |--------------------------------------------------------------------------
+ |
+ | Normal users will never need to edit this. This option lets you run
+ | Snipe-IT within an I-Frame, which is normally disabled by default for
+ | security reasons, to prevent clickjacking. It should normally be set to false.
+ |
+ */
'allow_iframing' => env('ALLOW_IFRAMING', false),
+ /*
+ |--------------------------------------------------------------------------
+ | ENABLE HTTP Strict Transport Security (HSTS)
+ |--------------------------------------------------------------------------
+ |
+ | This is set to default false for backwards compatibilty but should be
+ | set to true if the hosting environment allows it.
+ |
+ | See https://scotthelme.co.uk/hsts-the-missing-link-in-tls/
+ |
+ */
+
+ 'enable_hsts' => env('ENABLE_HSTS', false),
+
/*
|--------------------------------------------------------------------------
| REFERRER-POLICY
diff --git a/public/css/overrides.css b/public/css/overrides.css
index 0c23ba211b..50c9619ae8 100644
--- a/public/css/overrides.css
+++ b/public/css/overrides.css
@@ -1,2 +1,2 @@
-.skin-red .skin-purple .skin-blue .skin-black .skin-orange .skin-yellow .skin-green .skin-red-dark .skin-purple-dark .skin-blue-dark .skin-black-dark .skin-orange-dark .skin-yellow-dark .skin-green-dark .skin-contrast .main-header .logo{background-color:inherit}.main-header .logo{width:100%!important;white-space:nowrap;text-align:left;display:block;clear:both}.main-header .logo a:hover,.main-header .logoa:link,.main-header .logo a:visited{color:#fff}.huge{font-size:40px}.btn-file{position:relative;overflow:hidden}.dropdown-menu>li>a{color:#354044}#sort tr.cansort{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:3px;border-left:2px solid #e6e7e8;color:#444;cursor:move}.user-image-inline{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px}.input-group .input-group-addon{background-color:#f4f4f4}a.accordion-header{color:#333}.dynamic-form-row{padding:10px;margin:20px}.handle{padding-left:10px}.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;outline:none;background:#fff;cursor:inherit;display:block}.main-footer{font-size:13px}.main-header{max-height:150px}.navbar-nav>.user-menu>.dropdown-menu{width:inherit}.main-header .logo{padding:0 5px 0 15px}.sidebar-toggle{margin-left:-48px;z-index:100;background-color:inherit}.sidebar-toggle-mobile{z-index:100;width:50px;padding-top:10px}.main-header .sidebar-toggle:before{content:"\F0C9"}.direct-chat-contacts{padding:10px;height:150px}.select2-container{width:100%}.error input{color:#dd4b39;border:2px solid #dd4b39!important}.alert-msg,.error label{color:#dd4b39;display:block}.input-group[class*=col-]{padding-right:15px;padding-left:15px}.control-label.multiline{padding-top:10px}.btn-outline{color:inherit;background-color:transparent;-webkit-transition:all .5s;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.slideout-menu{position:fixed;top:0;right:-250px;width:250px;height:100%;background:#333;z-index:100;margin-top:100px;color:#fff;padding:10px}.slideout-menu h3{position:relative;padding:5px;color:#fff;font-size:1.2em;font-weight:400;border-bottom:4px solid #222}.slideout-menu .slideout-menu-toggle{position:absolute;top:12px;right:10px;display:inline-block;padding:6px 9px 5px;font-family:Arial,sans-serif;font-weight:700;line-height:1;background:#222;color:#999;text-decoration:none;vertical-align:top}.slideout-menu .slideout-menu-toggle:hover{color:#fff}.slideout-menu ul{list-style:none;font-weight:300;border-top:1px solid #151515;border-bottom:1px solid #454545}.slideout-menu ul li{border-top:1px solid #454545;border-bottom:1px solid #151515}.slideout-menu ul li a{position:relative;display:block;padding:10px;color:#999;text-decoration:none}.slideout-menu ul li a:hover{background:#000;color:#fff}.slideout-menu ul li a i{position:absolute;top:15px;right:10px;opacity:.5}.btn-box-tool-lg{font-size:16px;color:orange}.bs-wizard{margin-top:20px;border-bottom:1px solid #e0e0e0;padding:0 0 10px}.bs-wizard>.bs-wizard-step{padding:0;position:relative}.bs-wizard>.bs-wizard-step .bs-wizard-stepnum{color:#595959;font-size:16px;margin-bottom:5px}.bs-wizard>.bs-wizard-step .bs-wizard-info{color:#999;font-size:14px}.bs-wizard>.bs-wizard-step>.bs-wizard-dot{position:absolute;width:30px;height:30px;display:block;background:#fbe8aa;top:45px;left:50%;margin-top:-15px;margin-left:-15px;border-radius:50%}.bs-wizard>.bs-wizard-step>.bs-wizard-dot:after{content:" ";width:14px;height:14px;background:#fbbd19;border-radius:50px;position:absolute;top:8px;left:8px}.bs-wizard>.bs-wizard-step>.progress{position:relative;border-radius:0;height:8px;-webkit-box-shadow:none;box-shadow:none;margin:20px 0}.bs-wizard>.bs-wizard-step>.progress>.progress-bar{width:0;-webkit-box-shadow:none;box-shadow:none;background:#fbe8aa}.bs-wizard>.bs-wizard-step.complete>.progress>.progress-bar{width:100%}.bs-wizard>.bs-wizard-step.active>.progress>.progress-bar{width:50%}.bs-wizard>.bs-wizard-step:first-child.active>.progress>.progress-bar{width:0}.bs-wizard>.bs-wizard-step:last-child.active>.progress>.progress-bar{width:100%}.bs-wizard>.bs-wizard-step.disabled>.bs-wizard-dot{background-color:#f5f5f5}.bs-wizard>.bs-wizard-step.disabled>.bs-wizard-dot:after{opacity:0}.bs-wizard>.bs-wizard-step:first-child>.progress{left:50%;width:50%}.bs-wizard>.bs-wizard-step:last-child>.progress{width:50%}.bs-wizard>.bs-wizard-step.disabled a.bs-wizard-dot{pointer-events:none}.left-navblock{display:inline-block;float:left;text-align:left;color:#fff;padding:0}.skin-red .skin-purple .skin-blue .skin-black .skin-orange .skin-yellow .skin-green .skin-red-dark .skin-purple-dark .skin-blue-dark .skin-black-dark .skin-orange-dark .skin-yellow-dark .skin-green-dark .skin-contrast .main-header .navbar .dropdown-menu li a{color:#333}a.logo.no-hover a:hover{background-color:transparent}.required{border-right:6px solid orange}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:13px}.sidebar-menu{font-size:14px;white-space:normal}@media print{a[href]:after{content:none}.tab-content>.tab-pane{display:block!important;opacity:1!important;visibility:visible!important}}.navbar-brand>img,img.navbar-brand-img{float:left;padding:5px 5px 5px 0;max-height:50px}.input-daterange{border-radius:0}.btn.bg-maroon,.btn.bg-purple{min-width:90px}[hidden]{display:none!important}#toolbar{margin-top:10px}#uploadPreview{border:1px solid grey}.icon-med{font-size:20px;color:#889195}#login-logo{padding-top:20px;padding-bottom:10px;max-width:200px}a.skip-main{left:-999px;position:absolute;top:auto;width:1px;height:1px;overflow:hidden;z-index:-999}a.skip-main:active,a.skip-main:focus{color:#fff;background-color:#000;left:auto;top:auto;width:30%;height:auto;overflow:auto;margin:10px 35%;padding:5px;border-radius:15px;border:4px solid #ff0;text-align:center;font-size:1.2em;z-index:999}h2{font-size:22px}h3{font-size:20px}h4{font-size:16px}.row-striped{vertical-align:top;line-height:2.6;padding:0;margin-left:20px;-webkit-box-sizing:border-box;box-sizing:border-box;display:table}.row-striped .row:nth-of-type(odd) div{background-color:#f9f9f9;border-top:1px solid #ddd;display:table-cell}.row-striped .row:nth-of-type(2n) div{background:#fff;border-top:1px solid #ddd;display:table-cell}
+.skin-red .skin-purple .skin-blue .skin-black .skin-orange .skin-yellow .skin-green .skin-red-dark .skin-purple-dark .skin-blue-dark .skin-black-dark .skin-orange-dark .skin-yellow-dark .skin-green-dark .skin-contrast .main-header .logo{background-color:inherit}.main-header .logo{width:100%!important;white-space:nowrap;text-align:left;display:block;clear:both}.main-header .logo a:hover,.main-header .logoa:link,.main-header .logo a:visited{color:#fff}.huge{font-size:40px}.btn-file{position:relative;overflow:hidden}.dropdown-menu>li>a{color:#354044}#sort tr.cansort{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:3px;border-left:2px solid #e6e7e8;color:#444;cursor:move}.user-image-inline{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px}.input-group .input-group-addon{background-color:#f4f4f4}a.accordion-header{color:#333}.dynamic-form-row{padding:10px;margin:20px}.handle{padding-left:10px}.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;outline:none;background:#fff;cursor:inherit;display:block}.main-footer{font-size:13px}.main-header{max-height:150px}.navbar-nav>.user-menu>.dropdown-menu{width:inherit}.main-header .logo{padding:0 5px 0 15px}.sidebar-toggle{margin-left:-48px;z-index:100;background-color:inherit}.sidebar-toggle-mobile{z-index:100;width:50px;padding-top:10px}.main-header .sidebar-toggle:before{content:"\F0C9"}.direct-chat-contacts{padding:10px;height:150px}.select2-container{width:100%}.error input{color:#dd4b39;border:2px solid #dd4b39!important}.alert-msg,.error label{color:#dd4b39;display:block}.input-group[class*=col-]{padding-right:15px;padding-left:15px}.control-label.multiline{padding-top:10px}.btn-outline{color:inherit;background-color:transparent;-webkit-transition:all .5s;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.slideout-menu{position:fixed;top:0;right:-250px;width:250px;height:100%;background:#333;z-index:100;margin-top:100px;color:#fff;padding:10px}.slideout-menu h3{position:relative;padding:5px;color:#fff;font-size:1.2em;font-weight:400;border-bottom:4px solid #222}.slideout-menu .slideout-menu-toggle{position:absolute;top:12px;right:10px;display:inline-block;padding:6px 9px 5px;font-family:Arial,sans-serif;font-weight:700;line-height:1;background:#222;color:#999;text-decoration:none;vertical-align:top}.slideout-menu .slideout-menu-toggle:hover{color:#fff}.slideout-menu ul{list-style:none;font-weight:300;border-top:1px solid #151515;border-bottom:1px solid #454545}.slideout-menu ul li{border-top:1px solid #454545;border-bottom:1px solid #151515}.slideout-menu ul li a{position:relative;display:block;padding:10px;color:#999;text-decoration:none}.slideout-menu ul li a:hover{background:#000;color:#fff}.slideout-menu ul li a i{position:absolute;top:15px;right:10px;opacity:.5}.btn-box-tool-lg{font-size:16px;color:orange}.bs-wizard{margin-top:20px;border-bottom:1px solid #e0e0e0;padding:0 0 10px}.bs-wizard>.bs-wizard-step{padding:0;position:relative}.bs-wizard>.bs-wizard-step .bs-wizard-stepnum{color:#595959;font-size:16px;margin-bottom:5px}.bs-wizard>.bs-wizard-step .bs-wizard-info{color:#999;font-size:14px}.bs-wizard>.bs-wizard-step>.bs-wizard-dot{position:absolute;width:30px;height:30px;display:block;background:#fbe8aa;top:45px;left:50%;margin-top:-15px;margin-left:-15px;border-radius:50%}.bs-wizard>.bs-wizard-step>.bs-wizard-dot:after{content:" ";width:14px;height:14px;background:#fbbd19;border-radius:50px;position:absolute;top:8px;left:8px}.bs-wizard>.bs-wizard-step>.progress{position:relative;border-radius:0;height:8px;-webkit-box-shadow:none;box-shadow:none;margin:20px 0}.bs-wizard>.bs-wizard-step>.progress>.progress-bar{width:0;-webkit-box-shadow:none;box-shadow:none;background:#fbe8aa}.bs-wizard>.bs-wizard-step.complete>.progress>.progress-bar{width:100%}.bs-wizard>.bs-wizard-step.active>.progress>.progress-bar{width:50%}.bs-wizard>.bs-wizard-step:first-child.active>.progress>.progress-bar{width:0}.bs-wizard>.bs-wizard-step:last-child.active>.progress>.progress-bar{width:100%}.bs-wizard>.bs-wizard-step.disabled>.bs-wizard-dot{background-color:#f5f5f5}.bs-wizard>.bs-wizard-step.disabled>.bs-wizard-dot:after{opacity:0}.bs-wizard>.bs-wizard-step:first-child>.progress{left:50%;width:50%}.bs-wizard>.bs-wizard-step:last-child>.progress{width:50%}.bs-wizard>.bs-wizard-step.disabled a.bs-wizard-dot{pointer-events:none}.left-navblock{display:inline-block;float:left;text-align:left;color:#fff;padding:0}.skin-red .skin-purple .skin-blue .skin-black .skin-orange .skin-yellow .skin-green .skin-red-dark .skin-purple-dark .skin-blue-dark .skin-black-dark .skin-orange-dark .skin-yellow-dark .skin-green-dark .skin-contrast .main-header .navbar .dropdown-menu li a{color:#333}a.logo.no-hover a:hover{background-color:transparent}.required{border-right:6px solid orange}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:13px}.sidebar-menu{font-size:14px;white-space:normal}@media print{a[href]:after{content:none}.tab-content>.tab-pane{display:block!important;opacity:1!important;visibility:visible!important}}.navbar-brand>img,img.navbar-brand-img{float:left;padding:5px 5px 5px 0;max-height:50px}.input-daterange{border-radius:0}.btn.bg-maroon,.btn.bg-purple{min-width:90px}[hidden]{display:none!important}#toolbar{margin-top:10px}#uploadPreview{border:1px solid grey}.icon-med{font-size:20px;color:#889195}#login-logo{padding-top:20px;padding-bottom:10px;max-width:200px}a.skip-main{left:-999px;position:absolute;top:auto;width:1px;height:1px;overflow:hidden;z-index:-999}a.skip-main:active,a.skip-main:focus{color:#fff;background-color:#000;left:auto;top:auto;width:30%;height:auto;overflow:auto;margin:10px 35%;padding:5px;border-radius:15px;border:4px solid #ff0;text-align:center;font-size:1.2em;z-index:999}h2{font-size:22px}h3{font-size:20px}h4{font-size:16px}.row-striped{vertical-align:top;line-height:2.6;padding:0;margin-left:20px;-webkit-box-sizing:border-box;box-sizing:border-box;display:table}.row-striped .row:nth-of-type(odd) div{background-color:#f9f9f9;border-top:1px solid #ddd;display:table-cell}.row-striped .row:nth-of-type(2n) div{background:#fff;border-top:1px solid #ddd;display:table-cell}th.css-accessory>.th-inner,th.css-barcode>.th-inner,th.css-consumable>.th-inner,th.css-license>.th-inner{font-size:0;line-height:4!important;text-align:left;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}th.css-accessory>.th-inner:before,th.css-barcode>.th-inner:before,th.css-consumable>.th-inner:before,th.css-license>.th-inner:before,th.css-padlock>.th-inner:before{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:20px}th.css-padlock>.th-inner:before{content:"\F023";padding-right:2px}th.css-barcode>.th-inner:before{content:"\F02A"}th.css-license>.th-inner:before{content:"\F0C7"}th.css-consumable>.th-inner:before{content:"\F043"}th.css-accessory>.th-inner:before{content:"\F11C"}
/*# sourceMappingURL=overrides.css.map*/
\ No newline at end of file
diff --git a/public/css/overrides.css.map b/public/css/overrides.css.map
index 8f69f597f4..bfec6b1f14 100644
--- a/public/css/overrides.css.map
+++ b/public/css/overrides.css.map
@@ -1 +1 @@
-{"version":3,"sources":["webpack:///./resources/assets/less/overrides.less"],"names":[],"mappings":"AAAA,6OAkBE,yBAEF,mBACE,qBACA,mBACA,gBACA,cACA,WACA,iFACE,WAGJ,MACE,eAGF,UACI,kBACA,gBAGJ,oBACI,cAIJ,iBACA,kBACA,aACA,mBACA,kBACA,8BACA,WACA,YAGA,mBACE,WACA,WACA,YACA,kBACA,kBAGF,gCACE,yBAGF,mBACI,WAGJ,kBACE,aACA,YAIF,QACE,kBAGF,2BACI,kBACA,MACA,QACA,eACA,gBACA,gBACA,iBACA,wBACA,UACA,aACA,gBACA,eACA,cAEJ,aACE,eAEF,aACE,iBAIF,sCACE,cAEF,mBACE,qBAIF,gBACE,kBACA,YACA,yBAGF,uBACE,YACA,WACA,iBAwBF,oCACE,eAAS,CAGX,sBACE,aACA,aAIF,mBACE,WAEF,aACE,cACA,mCAGF,wBACE,cACA,cAGF,0BACQ,mBACA,kBAEN,yBACI,iBAGN,aACE,cACA,6BACA,8CAGF,yBACE,cAGF,yBACE,cAGF,sBACE,cAGF,yBACE,cAGF,wBACE,cAGF,uJACE,WAGF,eACC,eACA,MACA,aACA,YACA,YACA,gBACA,YACC,iBACA,WACA,aAEF,kBACC,kBACA,YACA,WACA,gBACA,gBACA,6BAED,qCACC,kBACA,SACA,WACA,qBACA,oBACA,6BACA,gBACA,cACA,gBACA,WACA,qBACA,mBAED,2CACC,WAED,kBACC,gBACA,gBACA,6BACA,gCAED,qBACC,6BACA,gCAED,uBACC,kBACA,cACA,aACA,WACA,qBAED,6BACC,gBACA,WAED,yBACC,kBACA,SACA,WACA,WAGA,iBACE,eACA,aAKF,WAAY,gBAGA,gCAAkC,iBAC9C,2BAA8B,UAAY,kBAE1C,8CAAiD,cAAgB,eAAiB,kBAClF,2CAA8C,WAAa,eAC3D,0CAA+C,kBAAoB,WAAa,YAAc,cAAgB,mBAAqB,SAAW,SAAW,iBAAmB,kBAAoB,kBAChM,gDAAqD,YAAc,WAAa,YAAc,mBAAqB,mBAAqB,kBAAoB,QAAU,SACtK,qCAA0C,kBAAoB,gBAAoB,WAAa,wCAAkB,cACjH,mDAA0D,QAAW,wCAAkB,mBACvF,4DAAmE,WACnE,0DAAiE,UACjE,sEAA6E,QAC7E,qEAA4E,WAC5E,mDAAwD,yBACxD,yDAA8D,UAC9D,iDAAuD,SAAW,UAClE,gDAAsD,UACtD,oDAAuD,oBAGxD,eACE,qBACA,WACA,gBACA,WACA,UAIF,mQAkBE,WAGF,wBACC,6BAID,UACE,8BAGF,KACE,oIAIA,eAGF,cACE,eACA,mBAGF,aACE,cACE,aAGF,uBACE,wBACA,oBACA,8BAIJ,uCACE,WACA,sBACA,gBAGF,iBACE,gBAGF,8BACE,eAGF,SACE,uBAGF,SACE,gBAGF,eACE,qBACA,CAIF,UACE,eACA,cAGF,YACE,iBACA,oBACA,gBAIF,YACE,YACA,kBACA,SACA,UACA,WACA,gBACA,aAEF,qCACE,WACA,sBACA,UACA,SACA,UACA,YACA,cACA,gBACA,YACA,mBACA,sBACA,kBACA,gBACA,YAGF,GACE,eAGF,GACE,eAGF,GACE,eAIF,aACE,mBACA,gBACA,UACA,iBACA,oDAGA,cAIF,uCACE,yBACA,0BACA,mBAIF,sCACE,gBACA,0BACA","file":"css/overrides.css","sourcesContent":[".skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n\n.logo {\n background-color: inherit;\n}\n.main-header .logo {\n width: 100% !important;\n white-space: nowrap;\n text-align: left;\n display: block;\n clear: both;\n &a:link, a:hover, a:visited {\n color: #fff\n }\n}\n.huge {\n font-size: 40px;\n}\n\n.btn-file {\n position: relative;\n overflow: hidden;\n}\n\n.dropdown-menu>li>a {\n color: #354044;\n}\n\n\n#sort tr.cansort {\nborder-radius: 2px;\npadding: 10px;\nbackground: #f4f4f4;\nmargin-bottom: 3px;\nborder-left: 2px solid #e6e7e8;\ncolor: #444;\ncursor: move;\n}\n\n.user-image-inline {\n float: left;\n width: 25px;\n height: 25px;\n border-radius: 50%;\n margin-right: 10px;\n}\n\n.input-group .input-group-addon {\n background-color: #f4f4f4;\n}\n\na.accordion-header {\n color: #333;\n}\n\n.dynamic-form-row {\n padding: 10px;\n margin: 20px;\n}\n\n\n.handle {\n padding-left: 10px;\n}\n\n.btn-file input[type=file] {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 100%;\n min-height: 100%;\n font-size: 100px;\n text-align: right;\n filter: alpha(opacity=0);\n opacity: 0;\n outline: none;\n background: white;\n cursor: inherit;\n display: block;\n}\n.main-footer {\n font-size: 13px;\n}\n.main-header {\n max-height: 150px;\n}\n\n\n.navbar-nav>.user-menu>.dropdown-menu {\n width: inherit;\n}\n.main-header .logo {\n padding: 0px 5px 0px 15px;\n}\n\n\n.sidebar-toggle {\n margin-left: -48px;\n z-index: 100;\n background-color: inherit;\n}\n\n.sidebar-toggle-mobile {\n z-index: 100;\n width: 50px;\n padding-top: 10px;\n}\n\n.skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n.navbar\n.dropdown-menu li a {\n //color: inherit;\n}\n\n.main-header .sidebar-toggle:before {\n content: \"\\f0c9\";\n}\n\n.direct-chat-contacts {\n padding: 10px;\n height: 150px;\n}\n\n\n.select2-container {\n width: 100%;\n}\n.error input {\n color: #dd4b39;\n border: 2px solid #dd4b39 !important;\n}\n\n.error label, .alert-msg {\n color: #dd4b39;\n display: block;\n}\n\n.input-group[class*=\"col-\"] {\n padding-right: 15px;\n padding-left: 15px;\n }\n .control-label.multiline {\n padding-top: 10px;\n }\n\n.btn-outline {\n color: inherit;\n background-color: transparent;\n transition: all .5s;\n}\n\n.btn-primary.btn-outline {\n color: #428bca;\n}\n\n.btn-success.btn-outline {\n color: #5cb85c;\n}\n\n.btn-info.btn-outline {\n color: #5bc0de;\n}\n\n.btn-warning.btn-outline {\n color: #f0ad4e;\n}\n\n.btn-danger.btn-outline {\n color: #d9534f;\n}\n\n.btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover {\n color: #fff;\n}\n\n.slideout-menu {\n\tposition: fixed;\n\ttop: 0;\n\tright: -250px;\n\twidth: 250px;\n\theight: 100%;\n\tbackground: #333;\n\tz-index: 100;\n margin-top: 100px;\n color: white;\n padding: 10px;\n}\n.slideout-menu h3 {\n\tposition: relative;\n\tpadding: 5px 5px;\n\tcolor: #fff;\n\tfont-size: 1.2em;\n\tfont-weight: 400;\n\tborder-bottom: 4px solid #222;\n}\n.slideout-menu .slideout-menu-toggle {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 10px;\n\tdisplay: inline-block;\n\tpadding: 6px 9px 5px;\n\tfont-family: Arial, sans-serif;\n\tfont-weight: bold;\n\tline-height: 1;\n\tbackground: #222;\n\tcolor: #999;\n\ttext-decoration: none;\n\tvertical-align: top;\n}\n.slideout-menu .slideout-menu-toggle:hover {\n\tcolor: #fff;\n}\n.slideout-menu ul {\n\tlist-style: none;\n\tfont-weight: 300;\n\tborder-top: 1px solid #151515;\n\tborder-bottom: 1px solid #454545;\n}\n.slideout-menu ul li {\n\tborder-top: 1px solid #454545;\n\tborder-bottom: 1px solid #151515;\n}\n.slideout-menu ul li a {\n\tposition: relative;\n\tdisplay: block;\n\tpadding: 10px;\n\tcolor: #999;\n\ttext-decoration: none;\n}\n.slideout-menu ul li a:hover {\n\tbackground: #000;\n\tcolor: #fff;\n}\n.slideout-menu ul li a i {\n\tposition: absolute;\n\ttop: 15px;\n\tright: 10px;\n\topacity: .5;\n}\n\n .btn-box-tool-lg {\n font-size: 16px;\n color: orange;\n }\n\n\n\n .bs-wizard {margin-top: 20px;}\n\n /*Form Wizard*/\n .bs-wizard {border-bottom: solid 1px #e0e0e0; padding: 0 0 10px 0;}\n .bs-wizard > .bs-wizard-step {padding: 0; position: relative;}\n .bs-wizard > .bs-wizard-step + .bs-wizard-step {}\n .bs-wizard > .bs-wizard-step .bs-wizard-stepnum {color: #595959; font-size: 16px; margin-bottom: 5px;}\n .bs-wizard > .bs-wizard-step .bs-wizard-info {color: #999; font-size: 14px;}\n .bs-wizard > .bs-wizard-step > .bs-wizard-dot {position: absolute; width: 30px; height: 30px; display: block; background: #fbe8aa; top: 45px; left: 50%; margin-top: -15px; margin-left: -15px; border-radius: 50%;}\n .bs-wizard > .bs-wizard-step > .bs-wizard-dot:after {content: ' '; width: 14px; height: 14px; background: #fbbd19; border-radius: 50px; position: absolute; top: 8px; left: 8px; }\n .bs-wizard > .bs-wizard-step > .progress {position: relative; border-radius: 0px; height: 8px; box-shadow: none; margin: 20px 0;}\n .bs-wizard > .bs-wizard-step > .progress > .progress-bar {width:0px; box-shadow: none; background: #fbe8aa;}\n .bs-wizard > .bs-wizard-step.complete > .progress > .progress-bar {width:100%;}\n .bs-wizard > .bs-wizard-step.active > .progress > .progress-bar {width:50%;}\n .bs-wizard > .bs-wizard-step:first-child.active > .progress > .progress-bar {width:0%;}\n .bs-wizard > .bs-wizard-step:last-child.active > .progress > .progress-bar {width: 100%;}\n .bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot {background-color: #f5f5f5;}\n .bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot:after {opacity: 0;}\n .bs-wizard > .bs-wizard-step:first-child > .progress {left: 50%; width: 50%;}\n .bs-wizard > .bs-wizard-step:last-child > .progress {width: 50%;}\n .bs-wizard > .bs-wizard-step.disabled a.bs-wizard-dot{ pointer-events: none; }\n /*END Form Wizard*/\n\n.left-navblock {\n display: inline-block;\n float: left;\n text-align: left;\n color: white;\n padding: 0px;\n /* adjust based on your layout */\n\n}\n.skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n.navbar\n.dropdown-menu li a {\n color: #333;\n}\n\na.logo.no-hover a:hover {\n background-color: transparent;\n}\n\n\n.required {\n border-right: 6px solid orange;\n}\n\nbody {\n font-family: -apple-system, BlinkMacSystemFont,\n \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\",\n \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n font-size: 13px;\n}\n\n.sidebar-menu {\n font-size: 14px;\n white-space: normal;\n}\n\n@media print {\n a[href]:after {\n content: none;\n }\n\n .tab-content > .tab-pane {\n display: block !important;\n opacity: 1 !important;\n visibility: visible !important;\n }\n}\n\nimg.navbar-brand-img, .navbar-brand>img {\n float: left;\n padding: 5px 5px 5px 0;\n max-height: 50px;\n}\n\n.input-daterange {\n border-radius: 0px;\n}\n\n.btn.bg-maroon, .btn.bg-purple{\n min-width:90px;\n}\n\n[hidden] {\n display: none !important;\n}\n\n#toolbar {\n margin-top: 10px;\n}\n\n#uploadPreview {\n border-color: grey;\n border-width: 1px;\n border-style: solid\n}\n\n.icon-med {\n font-size: 20px;\n color: #889195;\n}\n\n#login-logo {\n padding-top: 20px;\n padding-bottom: 10px;\n max-width: 200px\n}\n\n// accessibility skip link\na.skip-main {\n left:-999px;\n position:absolute;\n top:auto;\n width:1px;\n height:1px;\n overflow:hidden;\n z-index:-999;\n}\na.skip-main:focus, a.skip-main:active {\n color: #fff;\n background-color:#000;\n left: auto;\n top: auto;\n width: 30%;\n height: auto;\n overflow:auto;\n margin: 10px 35%;\n padding:5px;\n border-radius: 15px;\n border:4px solid yellow;\n text-align:center;\n font-size:1.2em;\n z-index:999;\n}\n\nh2 {\n font-size: 22px;\n}\n\nh3 {\n font-size: 20px;\n}\n\nh4 {\n font-size: 16px;\n}\n\n\n.row-striped {\n vertical-align: top;\n line-height: 2.6;\n padding: 0px;\n margin-left: 20px;\n box-sizing: border-box;\n //border-left: 1px solid #dddddd;\n //border-right: 1px solid #dddddd;\n display: table;\n\n}\n\n.row-striped .row:nth-of-type(odd) div {\n background-color: #f9f9f9;\n border-top: 1px solid #dddddd;\n display: table-cell;\n\n}\n\n.row-striped .row:nth-of-type(even) div {\n background: #FFFFFF;\n border-top: 1px solid #dddddd;\n display: table-cell;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./resources/assets/less/overrides.less"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"sources":["webpack:///./resources/assets/less/overrides.less"],"names":[],"mappings":"AAAA,6OAkBE,yBAEF,mBACE,qBACA,mBACA,gBACA,cACA,WACA,iFACE,WAGJ,MACE,eAGF,UACI,kBACA,gBAGJ,oBACI,cAIJ,iBACA,kBACA,aACA,mBACA,kBACA,8BACA,WACA,YAGA,mBACE,WACA,WACA,YACA,kBACA,kBAGF,gCACE,yBAGF,mBACI,WAGJ,kBACE,aACA,YAIF,QACE,kBAGF,2BACI,kBACA,MACA,QACA,eACA,gBACA,gBACA,iBACA,wBACA,UACA,aACA,gBACA,eACA,cAEJ,aACE,eAEF,aACE,iBAIF,sCACE,cAEF,mBACE,qBAIF,gBACE,kBACA,YACA,yBAGF,uBACE,YACA,WACA,iBAwBF,oCACE,eAAS,CAGX,sBACE,aACA,aAIF,mBACE,WAEF,aACE,cACA,mCAGF,wBACE,cACA,cAGF,0BACQ,mBACA,kBAEN,yBACI,iBAGN,aACE,cACA,6BACA,8CAGF,yBACE,cAGF,yBACE,cAGF,sBACE,cAGF,yBACE,cAGF,wBACE,cAGF,uJACE,WAGF,eACC,eACA,MACA,aACA,YACA,YACA,gBACA,YACC,iBACA,WACA,aAEF,kBACC,kBACA,YACA,WACA,gBACA,gBACA,6BAED,qCACC,kBACA,SACA,WACA,qBACA,oBACA,6BACA,gBACA,cACA,gBACA,WACA,qBACA,mBAED,2CACC,WAED,kBACC,gBACA,gBACA,6BACA,gCAED,qBACC,6BACA,gCAED,uBACC,kBACA,cACA,aACA,WACA,qBAED,6BACC,gBACA,WAED,yBACC,kBACA,SACA,WACA,WAGA,iBACE,eACA,aAKF,WAAY,gBAGA,gCAAkC,iBAC9C,2BAA8B,UAAY,kBAE1C,8CAAiD,cAAgB,eAAiB,kBAClF,2CAA8C,WAAa,eAC3D,0CAA+C,kBAAoB,WAAa,YAAc,cAAgB,mBAAqB,SAAW,SAAW,iBAAmB,kBAAoB,kBAChM,gDAAqD,YAAc,WAAa,YAAc,mBAAqB,mBAAqB,kBAAoB,QAAU,SACtK,qCAA0C,kBAAoB,gBAAoB,WAAa,wCAAkB,cACjH,mDAA0D,QAAW,wCAAkB,mBACvF,4DAAmE,WACnE,0DAAiE,UACjE,sEAA6E,QAC7E,qEAA4E,WAC5E,mDAAwD,yBACxD,yDAA8D,UAC9D,iDAAuD,SAAW,UAClE,gDAAsD,UACtD,oDAAuD,oBAGxD,eACE,qBACA,WACA,gBACA,WACA,UAIF,mQAkBE,WAGF,wBACC,6BAID,UACE,8BAGF,KACE,oIAIA,eAGF,cACE,eACA,mBAGF,aACE,cACE,aAGF,uBACE,wBACA,oBACA,8BAIJ,uCACE,WACA,sBACA,gBAGF,iBACE,gBAGF,8BACE,eAGF,SACE,uBAGF,SACE,gBAGF,eACE,qBACA,CAIF,UACE,eACA,cAGF,YACE,iBACA,oBACA,gBAIF,YACE,YACA,kBACA,SACA,UACA,WACA,gBACA,aAEF,qCACE,WACA,sBACA,UACA,SACA,UACA,YACA,cACA,gBACA,YACA,mBACA,sBACA,kBACA,gBACA,YAGF,GACE,eAGF,GACE,eAGF,GACE,eAIF,aACE,mBACA,gBACA,UACA,iBACA,oDAGA,cAIF,uCACE,yBACA,0BACA,mBAIF,sCACE,gBACA,0BACA,mBAoBF,yGAKE,YACA,wBACA,gBACA,oBACA,mCACA,kCAIF,qKAOE,qBACA,6CACA,eAGF,gCAEE,gBACA,kBAGF,gCAEE,eAAS,CAGX,gCAEE,eAAS,CAGX,mCAEE,eAAS,CAGX,kCAEE,eAAS","file":"css/overrides.css","sourcesContent":[".skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n\n.logo {\n background-color: inherit;\n}\n.main-header .logo {\n width: 100% !important;\n white-space: nowrap;\n text-align: left;\n display: block;\n clear: both;\n &a:link, a:hover, a:visited {\n color: #fff\n }\n}\n.huge {\n font-size: 40px;\n}\n\n.btn-file {\n position: relative;\n overflow: hidden;\n}\n\n.dropdown-menu>li>a {\n color: #354044;\n}\n\n\n#sort tr.cansort {\nborder-radius: 2px;\npadding: 10px;\nbackground: #f4f4f4;\nmargin-bottom: 3px;\nborder-left: 2px solid #e6e7e8;\ncolor: #444;\ncursor: move;\n}\n\n.user-image-inline {\n float: left;\n width: 25px;\n height: 25px;\n border-radius: 50%;\n margin-right: 10px;\n}\n\n.input-group .input-group-addon {\n background-color: #f4f4f4;\n}\n\na.accordion-header {\n color: #333;\n}\n\n.dynamic-form-row {\n padding: 10px;\n margin: 20px;\n}\n\n\n.handle {\n padding-left: 10px;\n}\n\n.btn-file input[type=file] {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 100%;\n min-height: 100%;\n font-size: 100px;\n text-align: right;\n filter: alpha(opacity=0);\n opacity: 0;\n outline: none;\n background: white;\n cursor: inherit;\n display: block;\n}\n.main-footer {\n font-size: 13px;\n}\n.main-header {\n max-height: 150px;\n}\n\n\n.navbar-nav>.user-menu>.dropdown-menu {\n width: inherit;\n}\n.main-header .logo {\n padding: 0px 5px 0px 15px;\n}\n\n\n.sidebar-toggle {\n margin-left: -48px;\n z-index: 100;\n background-color: inherit;\n}\n\n.sidebar-toggle-mobile {\n z-index: 100;\n width: 50px;\n padding-top: 10px;\n}\n\n.skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n.navbar\n.dropdown-menu li a {\n //color: inherit;\n}\n\n.main-header .sidebar-toggle:before {\n content: \"\\f0c9\";\n}\n\n.direct-chat-contacts {\n padding: 10px;\n height: 150px;\n}\n\n\n.select2-container {\n width: 100%;\n}\n.error input {\n color: #dd4b39;\n border: 2px solid #dd4b39 !important;\n}\n\n.error label, .alert-msg {\n color: #dd4b39;\n display: block;\n}\n\n.input-group[class*=\"col-\"] {\n padding-right: 15px;\n padding-left: 15px;\n }\n .control-label.multiline {\n padding-top: 10px;\n }\n\n.btn-outline {\n color: inherit;\n background-color: transparent;\n transition: all .5s;\n}\n\n.btn-primary.btn-outline {\n color: #428bca;\n}\n\n.btn-success.btn-outline {\n color: #5cb85c;\n}\n\n.btn-info.btn-outline {\n color: #5bc0de;\n}\n\n.btn-warning.btn-outline {\n color: #f0ad4e;\n}\n\n.btn-danger.btn-outline {\n color: #d9534f;\n}\n\n.btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover {\n color: #fff;\n}\n\n.slideout-menu {\n\tposition: fixed;\n\ttop: 0;\n\tright: -250px;\n\twidth: 250px;\n\theight: 100%;\n\tbackground: #333;\n\tz-index: 100;\n margin-top: 100px;\n color: white;\n padding: 10px;\n}\n.slideout-menu h3 {\n\tposition: relative;\n\tpadding: 5px 5px;\n\tcolor: #fff;\n\tfont-size: 1.2em;\n\tfont-weight: 400;\n\tborder-bottom: 4px solid #222;\n}\n.slideout-menu .slideout-menu-toggle {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 10px;\n\tdisplay: inline-block;\n\tpadding: 6px 9px 5px;\n\tfont-family: Arial, sans-serif;\n\tfont-weight: bold;\n\tline-height: 1;\n\tbackground: #222;\n\tcolor: #999;\n\ttext-decoration: none;\n\tvertical-align: top;\n}\n.slideout-menu .slideout-menu-toggle:hover {\n\tcolor: #fff;\n}\n.slideout-menu ul {\n\tlist-style: none;\n\tfont-weight: 300;\n\tborder-top: 1px solid #151515;\n\tborder-bottom: 1px solid #454545;\n}\n.slideout-menu ul li {\n\tborder-top: 1px solid #454545;\n\tborder-bottom: 1px solid #151515;\n}\n.slideout-menu ul li a {\n\tposition: relative;\n\tdisplay: block;\n\tpadding: 10px;\n\tcolor: #999;\n\ttext-decoration: none;\n}\n.slideout-menu ul li a:hover {\n\tbackground: #000;\n\tcolor: #fff;\n}\n.slideout-menu ul li a i {\n\tposition: absolute;\n\ttop: 15px;\n\tright: 10px;\n\topacity: .5;\n}\n\n .btn-box-tool-lg {\n font-size: 16px;\n color: orange;\n }\n\n\n\n .bs-wizard {margin-top: 20px;}\n\n /*Form Wizard*/\n .bs-wizard {border-bottom: solid 1px #e0e0e0; padding: 0 0 10px 0;}\n .bs-wizard > .bs-wizard-step {padding: 0; position: relative;}\n .bs-wizard > .bs-wizard-step + .bs-wizard-step {}\n .bs-wizard > .bs-wizard-step .bs-wizard-stepnum {color: #595959; font-size: 16px; margin-bottom: 5px;}\n .bs-wizard > .bs-wizard-step .bs-wizard-info {color: #999; font-size: 14px;}\n .bs-wizard > .bs-wizard-step > .bs-wizard-dot {position: absolute; width: 30px; height: 30px; display: block; background: #fbe8aa; top: 45px; left: 50%; margin-top: -15px; margin-left: -15px; border-radius: 50%;}\n .bs-wizard > .bs-wizard-step > .bs-wizard-dot:after {content: ' '; width: 14px; height: 14px; background: #fbbd19; border-radius: 50px; position: absolute; top: 8px; left: 8px; }\n .bs-wizard > .bs-wizard-step > .progress {position: relative; border-radius: 0px; height: 8px; box-shadow: none; margin: 20px 0;}\n .bs-wizard > .bs-wizard-step > .progress > .progress-bar {width:0px; box-shadow: none; background: #fbe8aa;}\n .bs-wizard > .bs-wizard-step.complete > .progress > .progress-bar {width:100%;}\n .bs-wizard > .bs-wizard-step.active > .progress > .progress-bar {width:50%;}\n .bs-wizard > .bs-wizard-step:first-child.active > .progress > .progress-bar {width:0%;}\n .bs-wizard > .bs-wizard-step:last-child.active > .progress > .progress-bar {width: 100%;}\n .bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot {background-color: #f5f5f5;}\n .bs-wizard > .bs-wizard-step.disabled > .bs-wizard-dot:after {opacity: 0;}\n .bs-wizard > .bs-wizard-step:first-child > .progress {left: 50%; width: 50%;}\n .bs-wizard > .bs-wizard-step:last-child > .progress {width: 50%;}\n .bs-wizard > .bs-wizard-step.disabled a.bs-wizard-dot{ pointer-events: none; }\n /*END Form Wizard*/\n\n.left-navblock {\n display: inline-block;\n float: left;\n text-align: left;\n color: white;\n padding: 0px;\n /* adjust based on your layout */\n\n}\n.skin-red\n.skin-purple\n.skin-blue\n.skin-black\n.skin-orange\n.skin-yellow\n.skin-green\n.skin-red-dark\n.skin-purple-dark\n.skin-blue-dark\n.skin-black-dark\n.skin-orange-dark\n.skin-yellow-dark\n.skin-green-dark\n.skin-contrast\n.main-header\n.navbar\n.dropdown-menu li a {\n color: #333;\n}\n\na.logo.no-hover a:hover {\n background-color: transparent;\n}\n\n\n.required {\n border-right: 6px solid orange;\n}\n\nbody {\n font-family: -apple-system, BlinkMacSystemFont,\n \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\",\n \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n font-size: 13px;\n}\n\n.sidebar-menu {\n font-size: 14px;\n white-space: normal;\n}\n\n@media print {\n a[href]:after {\n content: none;\n }\n\n .tab-content > .tab-pane {\n display: block !important;\n opacity: 1 !important;\n visibility: visible !important;\n }\n}\n\nimg.navbar-brand-img, .navbar-brand>img {\n float: left;\n padding: 5px 5px 5px 0;\n max-height: 50px;\n}\n\n.input-daterange {\n border-radius: 0px;\n}\n\n.btn.bg-maroon, .btn.bg-purple{\n min-width:90px;\n}\n\n[hidden] {\n display: none !important;\n}\n\n#toolbar {\n margin-top: 10px;\n}\n\n#uploadPreview {\n border-color: grey;\n border-width: 1px;\n border-style: solid\n}\n\n.icon-med {\n font-size: 20px;\n color: #889195;\n}\n\n#login-logo {\n padding-top: 20px;\n padding-bottom: 10px;\n max-width: 200px\n}\n\n// accessibility skip link\na.skip-main {\n left:-999px;\n position:absolute;\n top:auto;\n width:1px;\n height:1px;\n overflow:hidden;\n z-index:-999;\n}\na.skip-main:focus, a.skip-main:active {\n color: #fff;\n background-color:#000;\n left: auto;\n top: auto;\n width: 30%;\n height: auto;\n overflow:auto;\n margin: 10px 35%;\n padding:5px;\n border-radius: 15px;\n border:4px solid yellow;\n text-align:center;\n font-size:1.2em;\n z-index:999;\n}\n\nh2 {\n font-size: 22px;\n}\n\nh3 {\n font-size: 20px;\n}\n\nh4 {\n font-size: 16px;\n}\n\n\n.row-striped {\n vertical-align: top;\n line-height: 2.6;\n padding: 0px;\n margin-left: 20px;\n box-sizing: border-box;\n //border-left: 1px solid #dddddd;\n //border-right: 1px solid #dddddd;\n display: table;\n\n}\n\n.row-striped .row:nth-of-type(odd) div {\n background-color: #f9f9f9;\n border-top: 1px solid #dddddd;\n display: table-cell;\n\n}\n\n.row-striped .row:nth-of-type(even) div {\n background: #FFFFFF;\n border-top: 1px solid #dddddd;\n display: table-cell;\n}\n\n\n\n\n/**\n\n COLUMN SELECTOR ICONS\n -----------------------------\n This is kind of weird, but it is necessary to prevent the column-selector code from barfing, since\n any HTML used in the UserPresenter \"title\" attribute breaks the column selector HTML.\n\n Instead, we use CSS to add the icon into the table header, which leaves the column selector\n \"title\" text as-is.\n\n See https://github.com/snipe/snipe-it/issues/7989\n\n */\n\nth.css-barcode > .th-inner,\nth.css-license > .th-inner,\nth.css-consumable > .th-inner,\nth.css-accessory > .th-inner\n{\n font-size: 0px;\n line-height: 4!important;\n text-align: left;\n text-rendering: auto;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n\nth.css-padlock > .th-inner::before,\nth.css-barcode > .th-inner::before,\nth.css-license > .th-inner::before,\nth.css-consumable > .th-inner::before,\nth.css-accessory > .th-inner::before\n\n{\n display: inline-block;\n font: normal normal normal 14px/1 FontAwesome;\n font-size: 20px;\n}\n\nth.css-padlock > .th-inner::before\n{\n content: \"\\f023\";\n padding-right: 2px;\n}\n\nth.css-barcode > .th-inner::before\n{\n content: \"\\f02a\";\n}\n\nth.css-license > .th-inner::before\n{\n content: \"\\f0c7\";\n}\n\nth.css-consumable > .th-inner::before\n{\n content: \"\\f043\";\n}\n\nth.css-accessory > .th-inner::before\n{\n content: \"\\f11c\";\n}\n\n\n// WEBPACK FOOTER //\n// ./resources/assets/less/overrides.less"],"sourceRoot":""}
\ No newline at end of file
diff --git a/resources/assets/js/components/importer/importer-file.vue b/resources/assets/js/components/importer/importer-file.vue
index 4bcad9cce0..8dba9fe5d9 100644
--- a/resources/assets/js/components/importer/importer-file.vue
+++ b/resources/assets/js/components/importer/importer-file.vue
@@ -1,15 +1,9 @@
-
-
-
+
@@ -18,90 +12,84 @@ tr {
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ this.statusText }}
-
-
-
Header Field
-
Import Field
-
Sample Value
-
-
+
+
+
+
Header Field
+
Import Field
+
Sample Value
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ activeFile.first_row[index] }}
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
- {{ this.statusText }}
+
+
{{ activeFile.first_row[index] }}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ this.statusText }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/assets/js/components/passport/Clients.vue b/resources/assets/js/components/passport/Clients.vue
index 936786a87a..ee5db67742 100644
--- a/resources/assets/js/components/passport/Clients.vue
+++ b/resources/assets/js/components/passport/Clients.vue
@@ -83,7 +83,7 @@
-
+
Create Client
@@ -151,7 +151,7 @@
-
+
Edit Client
diff --git a/resources/assets/less/overrides.less b/resources/assets/less/overrides.less
index 21f8fc5012..d189a90796 100644
--- a/resources/assets/less/overrides.less
+++ b/resources/assets/less/overrides.less
@@ -466,6 +466,74 @@ h4 {
display: table-cell;
}
+
+
+
+/**
+
+ COLUMN SELECTOR ICONS
+ -----------------------------
+ This is kind of weird, but it is necessary to prevent the column-selector code from barfing, since
+ any HTML used in the UserPresenter "title" attribute breaks the column selector HTML.
+
+ Instead, we use CSS to add the icon into the table header, which leaves the column selector
+ "title" text as-is.
+
+ See https://github.com/snipe/snipe-it/issues/7989
+
+ */
+
+th.css-barcode > .th-inner,
+th.css-license > .th-inner,
+th.css-consumable > .th-inner,
+th.css-accessory > .th-inner
+{
+ font-size: 0px;
+ line-height: 4!important;
+ text-align: left;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+
+th.css-padlock > .th-inner::before,
+th.css-barcode > .th-inner::before,
+th.css-license > .th-inner::before,
+th.css-consumable > .th-inner::before,
+th.css-accessory > .th-inner::before
+
+{
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: 20px;
+}
+
+th.css-padlock > .th-inner::before
+{
+ content: "\f023";
+ padding-right: 2px;
+}
+
+th.css-barcode > .th-inner::before
+{
+ content: "\f02a";
+}
+
+th.css-license > .th-inner::before
+{
+ content: "\f0c7";
+}
+
+th.css-consumable > .th-inner::before
+{
+ content: "\f043";
+}
+
+th.css-accessory > .th-inner::before
+{
+ content: "\f11c";
+}
.small-box .inner {
padding-left: 15px;
padding-right: 15px;
diff --git a/resources/lang/en-GB/general.php b/resources/lang/en-GB/general.php
index 6efb25c645..8a4e332ad8 100644
--- a/resources/lang/en-GB/general.php
+++ b/resources/lang/en-GB/general.php
@@ -88,6 +88,9 @@
'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith@example.com)',
'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)',
'first' => 'First',
+ 'firstnamelastname' => 'First Name Last Name (janesmith@example.com)',
+ 'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
+ 'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)', 'first' => 'First',
'first_name' => 'First Name',
'first_name_format' => 'First Name (jane@example.com)',
'files' => 'Files',
diff --git a/resources/lang/en/admin/licenses/message.php b/resources/lang/en/admin/licenses/message.php
index 3e7ee486c7..87a7c3d0b9 100644
--- a/resources/lang/en/admin/licenses/message.php
+++ b/resources/lang/en/admin/licenses/message.php
@@ -8,6 +8,7 @@ return array(
'owner_doesnt_match_asset' => 'The asset you are trying to associate with this license is owned by somene other than the person selected in the assigned to dropdown.',
'assoc_users' => 'This license is currently checked out to a user and cannot be deleted. Please check the license in first, and then try deleting again. ',
'select_asset_or_person' => 'You must select an asset or a user, but not both.',
+ 'not_found' => 'License not found',
'create' => array(
diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php
index 962bfb8341..27e456042a 100644
--- a/resources/lang/en/general.php
+++ b/resources/lang/en/general.php
@@ -91,6 +91,11 @@
'lastnamefirstinitial_format' => 'Last Name First Initial (smithj@example.com)',
'firstintial_dot_lastname_format' => 'First Initial Last Name (j.smith@example.com)',
'first' => 'First',
+ 'firstnamelastname' => 'First Name Last Name (janesmith@example.com)',
+ 'lastname_firstinitial' => 'Last Name First Initial (smith_j@example.com)',
+ 'firstinitial.lastname' => 'First Initial Last Name (j.smith@example.com)',
+ 'firstnamelastinitial' => 'First Name Last Initial (janes@example.com)',
+ 'first' => 'First',
'first_name' => 'First Name',
'first_name_format' => 'First Name (jane@example.com)',
'files' => 'Files',
diff --git a/resources/macros/macros.php b/resources/macros/macros.php
index 40b0921c6d..5474db204a 100644
--- a/resources/macros/macros.php
+++ b/resources/macros/macros.php
@@ -464,11 +464,14 @@ Form::macro('username_format', function ($name = "username_format", $selected =
$formats = array(
'firstname.lastname' => trans('general.firstname_lastname_format'),
- 'firstname_lastname' => trans('general.firstname_lastname_underscore_format'),
- 'filastname' => trans('general.filastname_format'),
- 'firstintial.lastname' => trans('general.firstintial_dot_lastname_format'),
'firstname' => trans('general.first_name_format'),
+ 'filastname' => trans('general.filastname_format'),
'lastnamefirstinitial' => trans('general.lastnamefirstinitial_format'),
+ 'firstname_lastname' => trans('general.firstname_lastname_underscore_format'),
+ 'firstinitial.lastname' => trans('general.firstinitial.lastname'),
+ 'lastname_firstinitial' => trans('general.lastname_firstinitial'),
+ 'firstnamelastname' => trans('general.firstnamelastname'),
+ 'firstnamelastinitial' => trans('general.firstnamelastinitial')
);
$select = '