Merge branch 'develop' into refactor/store_asset_form_request

This commit is contained in:
Spencer Long 2024-01-02 18:05:15 -06:00 committed by GitHub
commit 8546407083
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3898 changed files with 16521 additions and 15048 deletions

View file

@ -2997,6 +2997,15 @@
"contributions": [
"code"
]
},
{
"login": "coach1988",
"name": "coach1988",
"avatar_url": "https://avatars.githubusercontent.com/u/2565989?v=4",
"profile": "https://github.com/coach1988",
"contributions": [
"code"
]
}
]
}

View file

@ -6,7 +6,7 @@ APP_DEBUG=false
APP_KEY=ChangeMe
APP_URL=null
APP_TIMEZONE='UTC'
APP_LOCALE=en
APP_LOCALE='en-US'
MAX_RESULTS=500
# --------------------------------------------

View file

@ -6,7 +6,7 @@ APP_DEBUG=false
APP_KEY='base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU='
APP_URL='http://localhost:8000'
APP_TIMEZONE='US/Pacific'
APP_LOCALE=en
APP_LOCALE='en-US'
FILESYSTEM_DISK=local
# --------------------------------------------

View file

@ -6,7 +6,7 @@ APP_DEBUG=true
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en
APP_LOCALE='en-US'
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS

View file

@ -30,10 +30,10 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View file

@ -52,6 +52,6 @@ jobs:
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View file

@ -1,4 +1,4 @@
FROM alpine:3.17.3
FROM alpine:3.18.5
# Apache + PHP
RUN apk add --no-cache \
apache2 \

View file

@ -1,5 +1,7 @@
[![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-330-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-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
[![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) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
## Snipe-IT - Open Source Asset Management System
@ -146,7 +148,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View file

@ -66,6 +66,7 @@ class LdapSync extends Command
$ldap_result_dept = Setting::getSettings()->ldap_dept;
$ldap_result_manager = Setting::getSettings()->ldap_manager;
$ldap_default_group = Setting::getSettings()->ldap_default_group;
$search_base = Setting::getSettings()->ldap_base_dn;
try {
$ldapconn = Ldap::connectToLdap();
@ -83,26 +84,35 @@ class LdapSync extends Command
$summary = [];
try {
/**
* if a location ID has been specified, use that OU
*/
if ( $this->option('location_id') != '') {
foreach($this->option('location_id') as $location_id){
$location_ou= Location::where('id', '=', $location_id)->value('ldap_ou');
$location_ou = Location::where('id', '=', $location_id)->value('ldap_ou');
$search_base = $location_ou;
Log::debug('Importing users from specified location OU: \"'.$search_base.'\".');
}
}
else if ($this->option('base_dn') != '') {
/**
* Otherwise if a manual base DN has been specified, use that
*/
} elseif ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
Log::debug('Importing users from specified base DN: \"'.$search_base.'\".');
} else {
$search_base = null;
}
/**
* If a filter has been specified, use that
*/
if ($this->option('filter') != '') {
$results = Ldap::findLdapUsers($search_base, -1, $this->option('filter'));
} else {
$results = Ldap::findLdapUsers($search_base);
}
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = ['error' => true, 'error_message' => $e->getMessage(), 'summary' => []];

View file

@ -63,7 +63,7 @@ class ResetDemoSettings extends Command
$settings->date_display_format = 'D M d, Y';
$settings->time_display_format = 'g:iA';
$settings->thumbnail_max_h = '30';
$settings->locale = 'en';
$settings->locale = 'en-US';
$settings->version_footer = 'on';
$settings->support_footer = null;
$settings->saml_enabled = '0';
@ -78,7 +78,7 @@ class ResetDemoSettings extends Command
$settings->save();
if ($user = User::where('username', '=', 'admin')->first()) {
$user->locale = 'en';
$user->locale = 'en-US';
$user->save();
}

View file

@ -84,35 +84,36 @@ class RestoreFromBackup extends Command
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels',
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
'storage/private_uploads/consumables',
'storage/private_uploads/eula-pdfs',
'storage/private_uploads/imports',
'storage/private_uploads/assetmodels',
'storage/private_uploads/users',
'storage/private_uploads/licenses',
'storage/private_uploads/signatures',
'storage/private_uploads/users',
];
$private_files = [
'storage/oauth-private.key',
'storage/oauth-public.key',
];
$public_dirs = [
'public/uploads/accessories',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/categories',
'public/uploads/companies',
'public/uploads/components',
'public/uploads/categories',
'public/uploads/manufacturers',
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/consumables',
'public/uploads/departments',
'public/uploads/avatars',
'public/uploads/suppliers',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/locations',
'public/uploads/accessories',
'public/uploads/models',
'public/uploads/categories',
'public/uploads/avatars',
'public/uploads/manufacturers',
'public/uploads/models',
'public/uploads/suppliers',
];
$public_files = [

View file

@ -7,6 +7,7 @@ use App\Models\CustomField;
use App\Models\Setting;
use Artisan;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Encryption\Encrypter;
class RotateAppKey extends Command
@ -16,14 +17,17 @@ class RotateAppKey extends Command
*
* @var string
*/
protected $signature = 'snipeit:rotate-key';
protected $signature = 'snipeit:rotate-key
{previous_key? : The previous key to rotate from}
{--emergency : Emergency mode - rotate from .env APP_KEY to newly-generated one, modifying .env}
{--force : Skip interactive confirmation}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
protected $description = 'Rotates APP_KEY to a new value, optionally taking the previous key as an argument';
/**
* Create a new command instance.
@ -42,26 +46,42 @@ class RotateAppKey extends Command
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ")) {
//make sure they specify only exactly one of --emergency, or a filename. Not neither, and not both.
if ( (!$this->option('emergency') && !$this->argument('previous_key')) || ( $this->option('emergency') && $this->argument('previous_key'))) {
$this->error("Specify only one of --emergency, or an app key value, in order to rotate keys");
return 1;
}
if ( $this->option('emergency') ) {
$msg = "\n****************************************************\nTHIS WILL MODIFY YOUR APP_KEY AND DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND \nRE-ENCRYPT THEM WITH A NEWLY GENERATED KEY. \n\nThere is NO undo. \n\nMake SURE you have a database backup and a backup of your .env generated BEFORE running this command. \n\nIf you do not save the newly generated APP_KEY to your .env in this process, \nyour encrypted data will no longer be decryptable. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup and an .env backup? ";
} else {
$msg = "\n****************************************************\nTHIS WILL DE-CRYPT YOUR ENCRYPTED CUSTOM FIELDS AND RE-ENCRYPT THEM WITH YOUR\nAPP_KEY.\n\nThere is NO undo. \n\nMake SURE you have a database backup BEFORE running this command. \n\nAre you SURE you wish to continue, and have confirmed you have a database backup? ";
}
if ($this->option('force') || $this->confirm($msg)) {
// Get the existing app_key and ciphers
// We put them in a variable since we clear the cache partway through here.
$old_app_key = config('app.key');
$cipher = config('app.cipher');
if ($this->option('emergency')) {
$old_app_key = config('app.key');
$cipher = config('app.cipher');
// Generate a new one
Artisan::call('key:generate', ['--show' => true]);
$new_app_key = Artisan::output();
// Generate a new one
Artisan::call('key:generate', ['--show' => true]);
$new_app_key = trim(Artisan::output());
// Clear the config cache
Artisan::call('config:clear');
// Clear the config cache
Artisan::call('config:clear');
$this->warn('Your app cipher is: '.$cipher);
$this->warn('Your old APP_KEY is: '.$old_app_key);
$this->warn('Your new APP_KEY is: '.$new_app_key);
// Write the new app key to the .env file
$this->writeNewEnvironmentFileWith($new_app_key);
} elseif ($this->argument('previous_key')) {
$old_app_key = $this->argument('previous_key');
$cipher = config('app.cipher'); // just a guess?
$new_app_key = config('app.key');
}
// Write the new app key to the .env file
$this->writeNewEnvironmentFileWith($new_app_key);
$this->warn('Your app cipher is: ' . $cipher);
$this->warn('Your old APP_KEY is: ' . $old_app_key);
$this->warn('Your new APP_KEY is: ' . $new_app_key);
// Manually create an old encrypter instance using the old app key
// and also create a new encrypter instance so we can re-crypt the field
@ -75,8 +95,16 @@ class RotateAppKey extends Command
$assets = Asset::whereNotNull($field->db_column)->get();
foreach ($assets as $asset) {
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
$this->line('DECRYPTED: '.$field->db_column);
try {
$asset->{$field->db_column} = $oldEncrypter->decrypt($asset->{$field->db_column});
$this->line('DECRYPTED: ' . $field->db_column);
} catch (DecryptException $e) {
$this->line('Could not decrypt '. $field->db_column.' using "old key" - skipping...');
continue;
} catch (\Exception $e) {
$this->error("Error decrypting ".$field->db_column.", reason: ".$e->getMessage().". Aborting key rotation");
throw $e;
}
$asset->{$field->db_column} = $newEncrypter->encrypt($asset->{$field->db_column});
$this->line('ENCRYPTED: '.$field->db_column);
$asset->save();
@ -86,10 +114,14 @@ class RotateAppKey extends Command
// Handle the LDAP password if one is provided
$setting = Setting::first();
if ($setting->ldap_pword != '') {
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
$setting->save();
$this->warn('LDAP password has been re-encrypted.');
try {
$setting->ldap_pword = $oldEncrypter->decrypt($setting->ldap_pword);
$setting->ldap_pword = $newEncrypter->encrypt($setting->ldap_pword);
$setting->save();
$this->warn('LDAP password has been re-encrypted.');
} catch(DecryptException $e) {
$this->warn("Unable to decrypt old LDAP password; skipping");
}
}
} else {
$this->info('This operation has been canceled. No changes have been made.');
@ -106,7 +138,7 @@ class RotateAppKey extends Command
{
file_put_contents($this->laravel->environmentFilePath(), preg_replace(
$this->keyReplacementPattern(),
'APP_KEY='.$key,
'APP_KEY="'.$key.'"',
file_get_contents($this->laravel->environmentFilePath())
));
}
@ -118,7 +150,7 @@ class RotateAppKey extends Command
*/
protected function keyReplacementPattern()
{
$escaped = preg_quote('='.$this->laravel['config']['app.key'], '/');
$escaped = '="?'.preg_quote($this->laravel['config']['app.key'], '/').'"?';
return "/^APP_KEY{$escaped}/m";
}

View file

@ -9,6 +9,7 @@ use App\Notifications\ExpectedCheckinAdminNotification;
use App\Notifications\ExpectedCheckinNotification;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class SendExpectedCheckinAlerts extends Command
{
@ -50,6 +51,7 @@ class SendExpectedCheckinAlerts extends Command
foreach ($assets as $asset) {
if ($asset->assigned && $asset->checkedOutToUser()) {
Log::info('Sending ExpectedCheckinNotification to ' . $asset->assigned->email);
$asset->assigned->notify((new ExpectedCheckinNotification($asset)));
}
}

View file

@ -18,6 +18,63 @@ use Carbon\Carbon;
class Helper
{
/**
* This is only used for reversing the migration that updates the locale to the 5-6 letter codes from two
* letter codes. The normal dropdowns use the autoglossonyms in the language files located
* in resources/en-US/localizations.php.
*/
public static $language_map = [
'af' => 'af-ZA', // Afrikaans
'am' => 'am-ET', // Amharic
'ar' => 'ar-SA', // Arabic
'bg' => 'bg-BG', // Bulgarian
'ca' => 'ca-ES', // Catalan
'cs' => 'cs-CZ', // Czech
'cy' => 'cy-GB', // Welsh
'da' => 'da-DK', // Danish
'de-i' => 'de-if', // German informal
'de' => 'de-DE', // German
'el' => 'el-GR', // Greek
'en' => 'en-US', // English
'et' => 'et-EE', // Estonian
'fa' => 'fa-IR', // Persian
'fi' => 'fi-FI', // Finnish
'fil' => 'fil-PH', // Filipino
'fr' => 'fr-FR', // French
'he' => 'he-IL', // Hebrew
'hr' => 'hr-HR', // Croatian
'hu' => 'hu-HU', // Hungarian
'id' => 'id-ID', // Indonesian
'is' => 'is-IS', // Icelandic
'it' => 'it-IT', // Italian
'iu' => 'iu-NU', // Inuktitut
'ja' => 'ja-JP', // Japanese
'ko' => 'ko-KR', // Korean
'lt' => 'lt-LT', // Lithuanian
'lv' => 'lv-LV', // Latvian
'mi' => 'mi-NZ', // Maori
'mk' => 'mk-MK', // Macedonian
'mn' => 'mn-MN', // Mongolian
'ms' => 'ms-MY', // Malay
'nl' => 'nl-NL', // Dutch
'no' => 'no-NO', // Norwegian
'pl' => 'pl-PL', // Polish
'ro' => 'ro-RO', // Romanian
'ru' => 'ru-RU', // Russian
'sk' => 'sk-SK', // Slovak
'sl' => 'sl-SI', // Slovenian
'so' => 'so-SO', // Somali
'ta' => 'ta-IN', // Tamil
'th' => 'th-TH', // Thai
'tl' => 'tl-PH', // Tagalog
'tr' => 'tr-TR', // Turkish
'uk' => 'uk-UA', // Ukrainian
'vi' => 'vi-VN', // Vietnamese
'zu' => 'zu-ZA', // Zulu
];
/**
* Simple helper to invoke the markdown parser
*
@ -354,7 +411,7 @@ class Helper
if ($index >= $total_colors) {
\Log::error('Status label count is '.$index.' and exceeds the allowed count of 266.');
\Log::info('Status label count is '.$index.' and exceeds the allowed count of 266.');
//patch fix for array key overflow (color count starts at 1, array starts at 0)
$index = $index - $total_colors - 1;
@ -1317,7 +1374,7 @@ class Helper
/*
* I know it's gauche to return a shitty HTML string, but this is just a helper and since it will be the same every single time,
* I know it's gauche to return a shitty HTML string, but this is just a helper and since it will be the same every single time,
* it seemed pretty safe to do here. Don't you judge me.
*/
public static function showDemoModeFieldWarning() {
@ -1325,4 +1382,55 @@ class Helper
return "<p class=\"text-warning\"><i class=\"fas fa-lock\"></i>" . trans('general.feature_disabled') . "</p>";
}
}
/**
* Ah, legacy code.
*
* This corrects the original mistakes from 2013 where we used the wrong locale codes. Hopefully we
* can get rid of this in a future version, but this should at least give us the belt and suspenders we need
* to be sure this change is not too disruptive.
*
* In this array, we ONLY include the older languages where we weren't using the correct locale codes.
*
* @see public static $language_map in this file
* @author A. Gianotto <snipe@snipe.net>
* @since 6.3.0
*
* @param $language_code
* @return string []
*/
public static function mapLegacyLocale($language_code = null)
{
if (strlen($language_code) > 4) {
return $language_code;
}
foreach (self::$language_map as $legacy => $new) {
if ($language_code == $legacy) {
\Log::debug('Current language is '.$legacy.', using '.$new.' instead');
return $new;
}
}
// Return US english if we don't have a match
return 'en-US';
}
public static function mapBackToLegacyLocale($new_locale = null)
{
if (strlen($new_locale) <= 4) {
return $new_locale; //"new locale" apparently wasn't quite so new
}
// This does a *reverse* search against our new language map array - given the value, find the *key* for it
$legacy_locale = array_search($new_locale, self::$language_map);
if($legacy_locale !== false) {
return $legacy_locale;
}
return $new_locale; // better that you have some weird locale that doesn't fit into our mappings anywhere than 'void'
}
}

View file

@ -60,9 +60,10 @@ class AccessoryCheckinController extends Controller
$this->authorize('checkin', $accessory);
$checkin_at = date('Y-m-d');
$checkin_hours = date('H:i:s');
$checkin_at = date('Y-m-d H:i:s');
if ($request->filled('checkin_at')) {
$checkin_at = $request->input('checkin_at');
$checkin_at = $request->input('checkin_at').' '.$checkin_hours;
}
// Was the accessory updated?

View file

@ -116,41 +116,17 @@ class AssetMaintenancesController extends Controller
{
$this->authorize('update', Asset::class);
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
if (! Company::isCurrentUserHasAccess($asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot add a maintenance for that asset'));
}
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->user_id = Auth::id();
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
}
$maintenance = new AssetMaintenance();
$maintenance->fill($request->all());
$maintenance->user_id = Auth::id();
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
}
@ -158,65 +134,39 @@ class AssetMaintenancesController extends Controller
* Validates and stores an update to an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $assetMaintenanceId
* @param int $id
* @param int $request
* @version v1.0
* @since [v4.0]
* @return string JSON
*/
public function update(Request $request, $assetMaintenanceId = null)
public function update(Request $request, $id)
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
}
if ($maintenance = AssetMaintenance::with('asset')->find($id)) {
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(request('asset_id'));
if (! Company::isCurrentUserHasAccess($asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
}
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
if (($assetMaintenance->completion_date == null)
) {
if (($assetMaintenance->asset_maintenance_time !== 0)
|| (! is_null($assetMaintenance->asset_maintenance_time))
) {
$assetMaintenance->asset_maintenance_time = null;
// Can this user manage this asset?
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
}
// The asset this miantenance is attached to is not valid or has been deleted
if (!$maintenance->asset) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('general.asset'), 'id' => $id])));
}
$maintenance->fill($request->all());
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
}
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id])));
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
}
/**

View file

@ -553,10 +553,11 @@ class AssetsController extends Controller
$asset = $request->handleImages($asset);
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
$model = AssetModel::find($request->get('model_id'));
$model = AssetModel::find($request->input('model_id'));
if (($model) && ($model->fieldset)) {
// Check that it's an object and not a collection
// (Sometimes people send arrays here and they shouldn't
if (($model) && ($model instanceof AssetModel) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
// Set the field value based on what was sent in the request
@ -864,13 +865,17 @@ class AssetsController extends Controller
public function checkin(Request $request, $asset_id)
{
$this->authorize('checkin', Asset::class);
$asset = Asset::findOrFail($asset_id);
$asset = Asset::with('model')->findOrFail($asset_id);
$this->authorize('checkin', $asset);
$target = $asset->assignedTo;
if (is_null($target)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in')));
return response()->json(Helper::formatStandardApiResponse('error', [
'asset_tag'=> e($asset->asset_tag),
'model' => e($asset->model->name),
'model_number' => e($asset->model->model_number)
], trans('admin/hardware/message.checkin.already_checked_in')));
}
$asset->expected_checkin = null;
@ -904,7 +909,11 @@ class AssetsController extends Controller
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag'=> e($asset->asset_tag),
'model' => e($asset->model->name),
'model_number' => e($asset->model->model_number)
], trans('admin/hardware/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));

View file

@ -40,6 +40,14 @@ class ReportsController extends Controller
$actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc');
}
if ($request->filled('action_source')) {
$actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc');
}
if ($request->filled('remote_ip')) {
$actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc');
}
if ($request->filled('uploads')) {
$actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc');
}
@ -52,6 +60,9 @@ class ReportsController extends Controller
'accept_signature',
'action_type',
'note',
'remote_ip',
'user_agent',
'action_source',
];

View file

@ -353,6 +353,7 @@ class UsersController extends Controller
$user = new User;
$user->fill($request->all());
$user->created_by = Auth::user()->id;
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
@ -706,11 +707,11 @@ class UsersController extends Controller
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/users/message.restore.success')), 200);
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored')), 200);
}
// Check validation to make sure we're not restoring a user with the same username as an existing user
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()])), 200);
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);

View file

@ -88,7 +88,7 @@ class AssetModelsController extends Controller
$model->requestable = Request::has('requestable');
if ($request->input('fieldset_id') != '') {
$model->fieldset_id = e($request->input('fieldset_id'));
$model->fieldset_id = $request->input('fieldset_id');
}
$model = $request->handleImages($model);
@ -101,7 +101,6 @@ class AssetModelsController extends Controller
}
}
// Redirect to the new model page
return redirect()->route('models.index')->with('success', trans('admin/models/message.create.success'));
}
@ -166,17 +165,14 @@ class AssetModelsController extends Controller
$this->removeCustomFieldsDefaultValues($model);
if ($request->input('fieldset_id') == '') {
$model->fieldset_id = null;
} else {
$model->fieldset_id = $request->input('fieldset_id');
$model->fieldset_id = $request->input('fieldset_id');
if ($this->shouldAddDefaultValues($request->input())) {
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))){
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
}
if ($this->shouldAddDefaultValues($request->input())) {
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))){
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
}
}

View file

@ -98,7 +98,7 @@ class AssetCheckoutController extends Controller
}
}
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
}

View file

@ -7,6 +7,8 @@ use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Statuslabel;
use App\Models\Setting;
use App\View\Label;
use Illuminate\Http\Request;
@ -23,6 +25,13 @@ class BulkAssetsController extends Controller
/**
* Display the bulk edit page.
*
* This method is super weird because it's kinda of like a controller within a controller.
* It's main function is to determine what the bulk action in, and then return a view with
* the information that view needs, be it bulk delete, bulk edit, restore, etc.
*
* This is something that made sense at the time, but sort of doesn't make sense now. A JS front-end to determine form
* action would make a lot more sense here and make things a lot more clear.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @return View
* @internal param int $assetId
@ -32,7 +41,10 @@ class BulkAssetsController extends Controller
public function edit(Request $request)
{
$this->authorize('view', Asset::class);
/**
* No asset IDs were passed
*/
if (! $request->filled('ids')) {
return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected'));
}
@ -41,59 +53,52 @@ class BulkAssetsController extends Controller
$bulk_back_url = request()->headers->get('referer');
session(['bulk_back_url' => $bulk_back_url]);
$asset_ids = array_values(array_unique($request->input('ids')));
//custom fields logic
$asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) {
return $query->where('fieldset_id', '!=', null);
})->get();
$models = $asset_custom_field->unique('model_id');
$asset_ids = $request->input('ids');
$assets = Asset::with('assignedTo', 'location', 'model')->find($asset_ids);
$models = $assets->unique('model_id');
$modelNames = [];
foreach($models as $model) {
$modelNames[] = $model->model->name;
}
}
if ($request->filled('bulk_actions')) {
switch ($request->input('bulk_actions')) {
case 'labels':
$this->authorize('view', Asset::class);
$assets_found = Asset::find($asset_ids);
if ($assets_found->isEmpty()){
return redirect()->back();
}
return (new Label)
->with('assets', $assets_found)
->with('assets', $assets)
->with('settings', Setting::getSettings())
->with('bulkedit', true)
->with('count', 0);
case 'delete':
$this->authorize('delete', Asset::class);
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
$assets->each(function ($asset) {
$this->authorize('delete', $asset);
$assets->each(function ($assets) {
$this->authorize('delete', $assets);
});
return view('hardware/bulk-delete')->with('assets', $assets);
case 'restore':
$this->authorize('update', Asset::class);
$assets = Asset::withTrashed()->find($asset_ids);
$assets = Asset::withTrashed()->find($asset_ids);
$assets->each(function ($asset) {
$this->authorize('delete', $asset);
});
return view('hardware/bulk-restore')->with('assets', $assets);
case 'edit':
$this->authorize('update', Asset::class);
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
->with('models', $models->pluck(['model']))
->with('models', $models->pluck(['model']))
->with('modelNames', $modelNames);
}
}
@ -117,25 +122,31 @@ class BulkAssetsController extends Controller
// Get the back url from the session and then destroy the session
$bulk_back_url = route('hardware.index');
if ($request->session()->has('bulk_back_url')) {
$bulk_back_url = $request->session()->pull('bulk_back_url');
}
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
if (Session::exists('ids')) {
$assets = Session::get('ids');
} elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) {
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
}
$assets = array_keys($request->input('ids'));
if ($request->anyFilled($custom_field_columns)) {
$custom_fields_present = true;
} else {
$custom_fields_present = false;
}
$assets = Asset::whereIn('id', array_keys($request->input('ids')))->get();
/**
* If ANY of these are filled, prepare to update the values on the assets.
*
* Additional checks will be needed for some of them to make sure the values
* make sense (for example, changing the status ID to something incompatible with
* its checkout status.
*/
if (($request->filled('purchase_date'))
|| ($request->filled('expected_checkin'))
|| ($request->filled('purchase_cost'))
@ -154,22 +165,32 @@ class BulkAssetsController extends Controller
|| ($request->anyFilled($custom_field_columns))
) {
foreach ($assets as $assetId) {
// Let's loop through those assets and build an update array
foreach ($assets as $asset) {
$this->update_array = [];
/**
* Leave out model_id and status here because we do math on that later. We have to do some extra
* validation and checks on those two.
*
* It's tempting to make these match the request check above, but some of these values require
* extra work to make sure the data makes sense.
*/
$this->conditionallyAddItem('purchase_date')
->conditionallyAddItem('expected_checkin')
->conditionallyAddItem('order_number')
->conditionallyAddItem('requestable')
->conditionallyAddItem('status_id')
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
}
/**
* Blank out fields that were requested to be blanked out via checkbox
*/
if ($request->input('null_purchase_date')=='1') {
$this->update_array['purchase_date'] = null;
}
@ -186,7 +207,6 @@ class BulkAssetsController extends Controller
$this->update_array['purchase_cost'] = $request->input('purchase_cost');
}
if ($request->filled('company_id')) {
$this->update_array['company_id'] = $request->input('company_id');
if ($request->input('company_id') == 'clear') {
@ -194,88 +214,129 @@ class BulkAssetsController extends Controller
}
}
/**
* We're trying to change the model ID - we need to do some extra checks here to make sure
* the custom field values work for the custom fieldset rules around this asset. Uniqueness
* and requiredness across the fieldset is particularly important, since those are
* fieldset-specific attributes.
*/
if ($request->filled('model_id')) {
$this->update_array['model_id'] = AssetModel::find($request->input('model_id'))->id;
}
/**
* We're trying to change the status ID - we need to do some extra checks here to
* make sure the status label type is one that makes sense for the state of the asset,
* for example, we shouldn't be able to make an asset archived if it's currently assigned
* to someone/something.
*/
if ($request->filled('status_id')) {
$updated_status = Statuslabel::find($request->input('status_id'));
// We cannot assign a non-deployable status type if the asset is already assigned.
// This could probably be added to a form request.
// If the asset isn't assigned, we don't care what the status is.
// Otherwise we need to make sure the status type is still a deployable one.
if (
($asset->assigned_to == '')
|| ($updated_status->deployable == '1') && ($asset->assetstatus->deployable == '1')
) {
$this->update_array['status_id'] = $updated_status->id;
}
}
/**
* We're changing the location ID - figure out which location we should apply
* this change to:
*
* 0 - RTD location only
* 1 - location ID and RTD location ID
* 2 - location ID only
*
* Note: this is kinda dumb and we should just use human-readable values IMHO. - snipe
*/
if ($request->filled('rtd_location_id')) {
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '0')) {
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
}
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
$this->update_array['location_id'] = $request->input('rtd_location_id');
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
}
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '2')) {
$this->update_array['location_id'] = $request->input('rtd_location_id');
}
}
/**
* ------------------------------------------------------------------------------
* ANYTHING that happens past this foreach
* WILL NOT BE logged in the edit log_meta data
* ------------------------------------------------------------------------------
*/
$changed = [];
$asset = Asset::find($assetId);
foreach ($this->update_array as $key => $value) {
if ($this->update_array[$key] != $asset->{$key}) {
$changed[$key]['old'] = $asset->{$key};
$changed[$key]['new'] = $this->update_array[$key];
}
}
if ($custom_fields_present) {
$model = $asset->model()->first();
/**
* Start all the custom fields shenanigans
*/
// Use the rules of the new model fieldsets if the model changed
if ($request->filled('model_id')) {
$this->update_array['model_id'] = $request->input('model_id');
$model = \App\Models\AssetModel::find($request->input('model_id'));
}
// Does the model have a fieldset?
if ($asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
// Make sure this model is valid
$assetCustomFields = ($model) ? $model->fieldset : null;
if ($assetCustomFields && $assetCustomFields->fields) {
foreach ($assetCustomFields->fields as $field) {
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted=='1')) {
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
/*
* Check if the decrypted existing value is different from one we just submitted
* and if not, pull it out of the object since it shouldn't really be updating at all.
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
* different times will have different values, so it will *look* like it was updated
* but it wasn't.
*/
if ($decrypted_old != $this->update_array[$field->db_column]) {
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
} else {
/*
* Remove the encrypted custom field from the update_array, since nothing changed
*/
unset($this->update_array[$field->db_column]);
unset($asset->{$field->db_column});
}
/*
* Check if the decrypted existing value is different from one we just submitted
* and if not, pull it out of the object since it shouldn't really be updating at all.
* If we don't do this, it will try to re-encrypt it, and the same value encrypted two
* different times will have different values, so it will *look* like it was updated
* but it wasn't.
*/
if ($decrypted_old != $this->update_array[$field->db_column]) {
$asset->{$field->db_column} = \Crypt::encrypt($this->update_array[$field->db_column]);
} else {
/*
* These custom fields aren't encrypted, just carry on as usual
* Remove the encrypted custom field from the update_array, since nothing changed
*/
unset($this->update_array[$field->db_column]);
unset($asset->{$field->db_column});
}
/*
* These custom fields aren't encrypted, just carry on as usual
*/
} else {
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
// Check if this is an array, and if so, flatten it
if (is_array($this->update_array[$field->db_column])) {
$asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]);
} else {
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
// Check if this is an array, and if so, flatten it
if (is_array($this->update_array[$field->db_column])) {
$asset->{$field->db_column} = implode(', ', $this->update_array[$field->db_column]);
} else {
$asset->{$field->db_column} = $this->update_array[$field->db_column];
}
}
$asset->{$field->db_column} = $this->update_array[$field->db_column];
}
}
}
} // endforeach
} // end custom field check
} // end custom fields handler
} // endforeach
}
// Check if it passes validation, and then try to save

View file

@ -56,7 +56,7 @@ class ProfileController extends Controller
$user->phone = $request->input('phone');
if (! config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en');
$user->locale = $request->input('locale', 'en-US');
}
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled == '1') && (! config('app.lock_passwords')))) {

View file

@ -252,6 +252,9 @@ class ReportsController extends Controller
trans('general.model_no'),
'To',
trans('general.notes'),
trans('admin/settings/general.login_ip'),
trans('admin/settings/general.login_user_agent'),
trans('general.action_source'),
'Changed',
];
@ -298,6 +301,9 @@ class ReportsController extends Controller
$target_name,
($actionlog->note) ? e($actionlog->note) : '',
$actionlog->log_meta,
$actionlog->remote_ip,
$actionlog->user_agent,
$actionlog->action_source,
];
fputcsv($handle, $row);
}

View file

@ -186,7 +186,7 @@ class SettingsController extends Controller
$settings->alerts_enabled = 1;
$settings->pwd_secure_min = 10;
$settings->brand = 1;
$settings->locale = $request->input('locale', 'en');
$settings->locale = $request->input('locale', 'en-US');
$settings->default_currency = $request->input('default_currency', 'USD');
$settings->user_id = 1;
$settings->email_domain = $request->input('email_domain');
@ -585,7 +585,7 @@ class SettingsController extends Controller
}
if (! config('app.lock_passwords')) {
$setting->locale = $request->input('locale', 'en');
$setting->locale = $request->input('locale', 'en-US');
}
$setting->default_currency = $request->input('default_currency', '$');
$setting->date_display_format = $request->input('date_display_format');

View file

@ -4,6 +4,7 @@ namespace App\Http\Middleware;
use App\Models\Setting;
use Closure;
use \App\Helpers\Helper;
class CheckLocale
{
@ -18,22 +19,28 @@ class CheckLocale
*/
public function handle($request, Closure $next, $guard = null)
{
// Default app settings from config
$language = config('app.locale');
if ($settings = Setting::getSettings()) {
// User's preference
if (($request->user()) && ($request->user()->locale)) {
\App::setLocale($request->user()->locale);
$language = $request->user()->locale;
// App setting preference
} elseif ($settings->locale != '') {
\App::setLocale($settings->locale);
// Default app setting
} else {
\App::setLocale(config('app.locale'));
$language = $settings->locale;
}
}
\App::setLocale(config('app.locale'));
}
if (config('app.locale') != Helper::mapLegacyLocale($language)) {
\Log::warning('Your current APP_LOCALE in your .env is set to "'.config('app.locale').'" and should be updated to be "'.Helper::mapLegacyLocale($language).'" in '.base_path().'/.env. Translations may display unexpectedly until this is updated.');
}
\App::setLocale(Helper::mapLegacyLocale($language));
return $next($request);
}
}

View file

@ -6,6 +6,7 @@ use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\Company;
use App\Models\Supplier;
use App\Models\Location;
@ -181,6 +182,9 @@ class ActionlogsTransformer
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'remote_ip' => ($actionlog->remote_ip) ?? null,
'user_agent' => ($actionlog->user_agent) ?? null,
'action_source' => ($actionlog->action_source) ?? null,
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
];
@ -212,6 +216,7 @@ class ActionlogsTransformer
{ $location = Location::withTrashed()->get();
$supplier = Supplier::withTrashed()->get();
$model = AssetModel::withTrashed()->get();
$status = Statuslabel::withTrashed()->get();
$company = Company::get();
@ -285,6 +290,19 @@ class ActionlogsTransformer
$clean_meta['Supplier'] = $clean_meta['supplier_id'];
unset($clean_meta['supplier_id']);
}
if(array_key_exists('status_id', $clean_meta)) {
$oldStatus = $status->find($clean_meta['status_id']['old']);
$oldStatusName = $oldStatus ? e($oldStatus->name) : trans('admin/statuslabels/message.deleted_label');
$newStatus = $status->find($clean_meta['status_id']['new']);
$newStatusName = $newStatus ? e($newStatus->name) : trans('admin/statuslabels/message.deleted_label');
$clean_meta['status_id']['old'] = $clean_meta['status_id']['old'] ? "[id: ".$clean_meta['status_id']['old']."] ". $oldStatusName : trans('general.unassigned');
$clean_meta['status_id']['new'] = $clean_meta['status_id']['new'] ? "[id: ".$clean_meta['status_id']['new']."] ". $newStatusName : trans('general.unassigned');
$clean_meta['Status'] = $clean_meta['status_id'];
unset($clean_meta['status_id']);
}
if(array_key_exists('asset_eol_date', $clean_meta)) {
$clean_meta['EOL date'] = $clean_meta['asset_eol_date'];
unset($clean_meta['asset_eol_date']);

View file

@ -25,7 +25,17 @@ class Actionlog extends SnipeModel
protected $table = 'action_logs';
public $timestamps = true;
protected $fillable = ['created_at', 'item_type', 'user_id', 'item_id', 'action_type', 'note', 'target_id', 'target_type', 'stored_eula'];
protected $fillable = [
'created_at',
'item_type',
'user_id',
'item_id',
'action_type',
'note',
'target_id',
'target_type',
'stored_eula'
];
use Searchable;
@ -34,7 +44,15 @@ class Actionlog extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = ['action_type', 'note', 'log_meta','user_id'];
protected $searchableAttributes = [
'action_type',
'note',
'log_meta',
'user_id',
'remote_ip',
'user_agent',
'action_source'
];
/**
* The relations and their attributes that should be included when searching the model.
@ -248,6 +266,9 @@ class Actionlog extends SnipeModel
public function logaction($actiontype)
{
$this->action_type = $actiontype;
$this->remote_ip = request()->ip();
$this->user_agent = request()->header('User-Agent');
$this->action_source = $this->determineActionSource();
if ($this->save()) {
return true;
@ -312,4 +333,29 @@ class Actionlog extends SnipeModel
->orderBy('created_at', 'asc')
->get();
}
/**
* Determines what the type of request is so we can log it to the action_log
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.3.0
* @return string
*/
public function determineActionSource() {
// This is an API call
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
&& (starts_with(request()->header('authorization'), 'Bearer '))) {
return 'api';
}
// This is probably NOT an API call
if (request()->filled('_token')) {
return 'gui';
}
// We're not sure, probably cli
return 'cli/unknown';
}
}

View file

@ -269,7 +269,7 @@ class Asset extends Depreciable
/**
* Determines if an asset is available for checkout.
* This checks to see if the it's checked out to an invalid (deleted) user
* This checks to see if it's checked out to an invalid (deleted) user
* OR if the assigned_to and deleted_at fields on the asset are empty AND
* that the status is deployable
*
@ -765,7 +765,7 @@ class Asset extends Depreciable
}
/**
* Establishes the asset -> status relationship
* Establishes the asset -> license seats relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
@ -933,6 +933,27 @@ class Asset extends Depreciable
return $cost;
}
/**
* -----------------------------------------------
* BEGIN MUTATORS
* -----------------------------------------------
**/
/**
* This sets the requestable to a boolean 0 or 1. This accounts for forms or API calls that
* explicitly pass the requestable field but it has a null or empty value.
*
* This will also correctly parse a 1/0 if "true"/"false" is passed.
*
* @param $value
* @return void
*/
public function setRequestableAttribute($value)
{
$this->attributes['requestable'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
/**
* -----------------------------------------------
* BEGIN QUERY SCOPES

View file

@ -20,10 +20,9 @@ class AssetMaintenance extends Model implements ICompanyableChild
use SoftDeletes;
use CompanyableChildTrait;
use ValidatingTrait;
protected $casts = [
'start_date' => 'datetime',
'completion_date' => 'datetime',
];
protected $table = 'asset_maintenances';
protected $rules = [
'asset_id' => 'required|integer',
@ -31,12 +30,31 @@ class AssetMaintenance extends Model implements ICompanyableChild
'asset_maintenance_type' => 'required',
'title' => 'required|max:100',
'is_warranty' => 'boolean',
'start_date' => 'required|date',
'completion_date' => 'nullable|date',
'start_date' => 'required|date_format:Y-m-d',
'completion_date' => 'date_format:Y-m-d|nullable',
'notes' => 'string|nullable',
'cost' => 'numeric|nullable',
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title',
'asset_id',
'supplier_id',
'asset_maintenance_type',
'is_warranty',
'start_date',
'completion_date',
'asset_maintenance_time',
'notes',
'cost',
];
use Searchable;
/**

View file

@ -86,5 +86,13 @@ class LabelWriter_30252 extends LabelWriter
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
$currentX, $barcodeSize + self::BARCODE_MARGIN, $usableWidth - self::TAG_SIZE, self::TAG_SIZE
);
}
}
}

View file

@ -74,12 +74,16 @@
"unicodeveloper/laravel-password": "^1.0",
"watson/validating": "^6.1"
},
"suggest": {
"ext-ldap": "*"
},
"require-dev": {
"brianium/paratest": "^6.6",
"fakerphp/faker": "^1.16",
"mockery/mockery": "^1.4",
"nunomaduro/larastan": "^1.0",
"nunomaduro/phpinsights": "^2.7",
"php-mock/php-mock-phpunit": "^2.8",
"phpunit/php-token-stream": "^3.1",
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.5",

687
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -99,7 +99,7 @@ return [
|
*/
'locale' => env('APP_LOCALE', 'en'),
'locale' => env('APP_LOCALE', 'en-US'),
/*
|--------------------------------------------------------------------------
@ -112,7 +112,7 @@ return [
|
*/
'fallback_locale' => 'en',
'fallback_locale' => 'en-US',
/*
|--------------------------------------------------------------------------

View file

@ -6,5 +6,5 @@ return array (
'prerelease_version' => '',
'hash_version' => 'g776b16d37',
'full_hash' => 'v6.2.4-pre-329-g776b16d37',
'branch' => 'develop',
'branch' => 'master',
);

View file

@ -99,10 +99,22 @@ class CustomFieldFactory extends Factory
return [
'name' => 'Test Checkbox',
'help_text' => 'This is a sample checkbox.',
'field_values' => "One\nTwo\nThree",
'field_values' => "One\r\nTwo\r\nThree",
'element' => 'checkbox',
];
});
}
public function testRadio()
{
return $this->state(function () {
return [
'name' => 'Test Radio',
'help_text' => 'This is a sample radio.',
'field_values' => "One\r\nTwo\r\nThree",
'element' => 'radio',
];
});
}
}

View file

@ -29,9 +29,9 @@ class SettingFactory extends Factory
'alerts_enabled' => true,
'brand' => 1,
'default_currency' => $this->faker->currencyCode(),
'locale' => 'en',
'locale' => 'en-US',
'pwd_secure_min' => 10, // Match web setup
'email_domain' => 'test.com',
'email_domain' => 'example.org',
];
}
}

View file

@ -27,7 +27,7 @@ class UserFactory extends Factory
'first_name' => $this->faker->firstName(),
'jobtitle' => $this->faker->jobTitle(),
'last_name' => $this->faker->lastName(),
'locale' => 'en',
'locale' => 'en-US',
'notes' => 'Created by DB seeder',
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'permissions' => '{}',

View file

@ -0,0 +1,45 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddImageToAssets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
/**
* This had to be changed back in time :(
*
* This migration was named stupidly in 2015, and we're now seeing weird namespace errors
* popping up in 2023, likely due to different laravel or PHP versions. This migration will run again on
* more updated systems, since the name of the migration has changed and therefore will look "new" to the
* migrations table/migration system, which is why we need to check if the
* field already exists. Thanks, I hate it. - snipe
*/
Schema::table('assets', function (Blueprint $table) {
if (!Schema::hasColumn('assets', 'image')) {
$table->text('image')->after('notes')->nullable()->default(null);
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
/**
* I'm leaving this one out, since it could destroy data that was already long-existing.
*/
}
}

View file

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class Image extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->text('image')->after('notes')->nullable()->default(null);
//
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('assets', function ($table) {
$table->dropColumn('image');
});
}
}

View file

@ -85,4 +85,4 @@ class DenormalizedEolAndAddColumnForExplicitDateToAssets extends Migration
// Default to MySQL's method
return DB::raw('DATE_ADD(purchase_date, INTERVAL ' . DB::getTablePrefix() . 'models.eol MONTH)');
}
}
}

View file

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddRemoteIpAndActionSourceToActionLogs extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->string('action_source')->nullable()->default(null);
$table->ipAddress('remote_ip')->nullable()->default(null);
$table->string('user_agent')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('action_logs', function (Blueprint $table) {
if (Schema::hasColumn('action_logs', 'action_source')) {
$table->dropColumn('action_source');
}
if (Schema::hasColumn('action_logs', 'remote_ip')) {
$table->dropColumn('remote_ip');
}
if (Schema::hasColumn('action_logs', 'user_agent')) {
$table->dropColumn('user_agent');
}
});
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIndexesToNewActivityReportFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->index('action_type');
$table->index('remote_ip');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->dropIndex(['action_type']);
$table->dropIndex(['remote_ip']);
});
}
}

View file

@ -0,0 +1,60 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use \App\Helpers\Helper;
use \App\Models\Setting;
use \App\Models\User;
class FixLanguageDirs extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
/**
* Update the settings table
*/
$settings = Setting::getSettings();
if (($settings) && ($settings->locale != '')) {
DB::table('settings')->update(['locale' => Helper::mapLegacyLocale($settings->locale)]);
}
/**
* Update the users table
*/
$users = User::whereNotNull('locale')->get();
// Skip the model in case the validation rules have changed
foreach ($users as $user) {
DB::table('users')->where('id', $user->id)->update(['locale' => Helper::mapLegacyLocale($user->locale)]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$settings = Setting::getSettings();
if (($settings) && ($settings->locale != '')) {
DB::table('settings')->update(['locale' => Helper::mapBackToLegacyLocale($settings->locale)]);
}
/**
* Update the users table
*/
$users = User::whereNotNull('locale')->whereNull('deleted_at')->get();
// Skip the model in case the validation rules have changed
foreach ($users as $user) {
DB::table('users')->where('id', $user->id)->update(['locale' => Helper::mapBackToLegacyLocale($user->locale)]);
}
}
}

View file

@ -35,6 +35,7 @@ class CustomFieldSeeder extends Seeder
CustomField::factory()->count(1)->macAddress()->create();
CustomField::factory()->count(1)->testEncrypted()->create();
CustomField::factory()->count(1)->testCheckbox()->create();
CustomField::factory()->count(1)->testRadio()->create();
DB::table('custom_field_custom_fieldset')->insert([
@ -96,6 +97,19 @@ class CustomFieldSeeder extends Seeder
'required' => 0,
],
[
'custom_field_id' => '8',
'custom_fieldset_id' => '2',
'order' => 0,
'required' => 0,
],
[
'custom_field_id' => '8',
'custom_fieldset_id' => '1',
'order' => 0,
'required' => 0,
],
]);
}
}

View file

@ -32,14 +32,14 @@ class SettingsSeeder extends Seeder
$settings->date_display_format = 'D M d, Y';
$settings->time_display_format = 'g:iA';
$settings->thumbnail_max_h = '30';
$settings->locale = 'en';
$settings->locale = 'en-US';
$settings->version_footer = 'on';
$settings->support_footer = 'on';
$settings->pwd_secure_min = '8';
$settings->save();
if ($user = User::where('username', '=', 'admin')->first()) {
$user->locale = 'en';
$user->locale = 'en-US';
$user->save();
}

157
package-lock.json generated
View file

@ -1329,9 +1329,9 @@
"dev": true
},
"@fortawesome/fontawesome-free": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
"integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg=="
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz",
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ=="
},
"@jridgewell/gen-mapping": {
"version": "0.1.1",
@ -1565,27 +1565,27 @@
}
},
"@types/eslint": {
"version": "8.44.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz",
"integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==",
"version": "8.44.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz",
"integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==",
"requires": {
"@types/estree": "*",
"@types/json-schema": "*"
}
},
"@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"requires": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
},
"@types/express": {
"version": "4.17.13",
@ -2078,9 +2078,9 @@
}
},
"acorn": {
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w=="
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw=="
},
"acorn-import-assertions": {
"version": "1.9.0",
@ -2201,9 +2201,9 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
},
"alpinejs": {
"version": "3.13.3",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
"integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
"version": "3.12.3",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.3.tgz",
"integrity": "sha512-fLz2dfYQ3xCk7Ip8LiIpV2W+9brUyex2TAE7Z0BCvZdUDklJE+n+a8gCgLWzfZ0GzZNZu7HUP8Z0z6Xbm6fsSA==",
"requires": {
"@vue/reactivity": "~3.1.1"
}
@ -3990,11 +3990,11 @@
"dev": true
},
"copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz",
"integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==",
"requires": {
"is-what": "^3.14.1"
"is-what": "^3.12.0"
}
},
"core-js": {
@ -4885,9 +4885,9 @@
}
},
"es-module-lexer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
"integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz",
"integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA=="
},
"es-to-primitive": {
"version": "1.2.1",
@ -15311,7 +15311,6 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
@ -15891,11 +15890,6 @@
"jquery": ">=1.8.0 <4.0.0"
}
},
"jquery-ui-bundle": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/jquery-ui-bundle/-/jquery-ui-bundle-1.12.1.tgz",
"integrity": "sha1-1r4uTDd0lOI3ixyuKSCpHRGC2MQ="
},
"jquery.iframe-transport": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/jquery.iframe-transport/-/jquery.iframe-transport-1.0.0.tgz",
@ -15972,9 +15966,9 @@
}
},
"jspdf-autotable": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.7.1.tgz",
"integrity": "sha512-5fgjqE8nIwUoNz5l/i/aD/uONKofE4yp/kJ097EKBllPVTPGnGV5OWHld30db3+CvNrgzrRl8gmTnKF6vag05g=="
"version": "3.5.31",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.31.tgz",
"integrity": "sha512-Lc1KuLGDQWW/5t57Z/+c2E94XQV3jV2QVU3xMRiwvcm/nMx79aCkpPCsxLzJZVFneZvz4XoA8+egQR1QajYiWw=="
},
"junk": {
"version": "3.1.0",
@ -16322,9 +16316,9 @@
}
},
"less": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
"integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz",
"integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==",
"requires": {
"copy-anything": "^2.0.1",
"errno": "^0.1.1",
@ -16332,12 +16326,34 @@
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"needle": "^2.5.2",
"parse-node-version": "^1.0.1",
"source-map": "~0.6.0",
"tslib": "^2.3.0"
},
"dependencies": {
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"optional": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"optional": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"optional": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -16488,24 +16504,6 @@
"yallist": "^4.0.0"
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"optional": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"dependencies": {
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"optional": true
}
}
},
"md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
@ -16804,13 +16802,13 @@
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
},
"needle": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz",
"integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==",
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
"integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
"optional": true,
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.6.3",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
@ -16823,15 +16821,6 @@
"ms": "^2.1.1"
}
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -17419,12 +17408,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"optional": true
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@ -19644,9 +19627,9 @@
}
},
"webpack": {
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"version": "5.88.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
@ -19685,18 +19668,18 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
"integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
"requires": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
"version": "7.0.12",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
},
"commander": {
"version": "2.20.3",
@ -19727,9 +19710,9 @@
}
},
"terser": {
"version": "5.24.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
"integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
"version": "5.19.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
"requires": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",

View file

@ -29,7 +29,7 @@
"acorn-import-assertions": "^1.9.0",
"admin-lte": "^2.4.18",
"ajv": "^6.12.6",
"alpinejs": "^3.13.2",
"alpinejs": "^3.13.3",
"blueimp-file-upload": "^9.34.0",
"bootstrap": "^3.4.1",
"bootstrap-colorpicker": "^2.5.3",
@ -44,7 +44,6 @@
"jquery-form-validator": "^2.3.79",
"jquery-slimscroll": "^1.3.8",
"jquery-ui": "^1.13.2",
"jquery-ui-bundle": "^1.12.1",
"jquery.iframe-transport": "^1.0.0",
"jspdf-autotable": "^3.7.1",
"less": "^4.2.0",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/dist/all.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -1,24 +1,24 @@
{
"/js/build/app.js": "/js/build/app.js?id=41293fc7aa00ece89fd524e1e0e31a68",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/build/overrides.css": "/css/build/overrides.css?id=1681749b20329c40a052156ea4cb42fe",
"/css/build/app.css": "/css/build/app.css?id=cc0fd2d77504fdd7f03e91e2369d02a9",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=dc383f8560a8d4adb51d44fb4043e03b",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=e6e53eef152bba01a4c666a4d8b01117",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=07273f6ca3c698a39e8fc2075af4fa07",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=c1f33574ecb9d3e69d9b8fe5bd68e101",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=7d92dea45d94be7e1d4e427c728d335d",
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4",
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=8ca888bbc050d9680cbb65021382acba",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=bdfc704731682c67645a2248b0b8d2d7",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=d419cb63a12dc175d71645c876bfc2ab",
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/all.css": "/css/dist/all.css?id=391b67f142742a6b11a92b042c6d475c",
"/js/build/app.js": "/js/build/app.js?id=72071a8a4dc754c61b0440d3c4119cbf",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/build/overrides.css": "/css/build/overrides.css?id=c8c36239d8ef77849c8c42c7edcb9d2e",
"/css/build/app.css": "/css/build/app.css?id=63bae107587455323cedd08aff2e387d",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=f25c77ed07053646a42e9c19923d24fa",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=9f944e8021781af1ce45d27765d1c0c2",
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=6cf460bed48ab738041f60231a3f005a",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/all.css": "/css/dist/all.css?id=6b75b5bab0fd7095d4663c903fd8b584",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=a656b2d865fe379d8851757e8e4001ef",
@ -30,23 +30,23 @@
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=70ad875b2378eb850254f01dec991ade",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=d36941873b661076f146b0221f13497d",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=2bd29fa7f9d666800c246a52ce708633",
"/js/build/vendor.js": "/js/build/vendor.js?id=917784d6fe54bcfe39656e0ded1b43e4",
"/js/build/vendor.js": "/js/build/vendor.js?id=a2b971da417306a63385c8098acfe4af",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41",
"/js/dist/all.js": "/js/dist/all.js?id=1a069f231b257b1ad4e3751302705f97",
"/js/dist/all.js": "/js/dist/all.js?id=256c2e7da22a51ce49b03ffa24527cee",
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=7f9a130eda6916eaa32a0a57e81918f3",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=d419cb63a12dc175d71645c876bfc2ab",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=bdfc704731682c67645a2248b0b8d2d7",
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=07273f6ca3c698a39e8fc2075af4fa07",
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=c1f33574ecb9d3e69d9b8fe5bd68e101",
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=8ca888bbc050d9680cbb65021382acba",
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=6fe68325d5356197672c27bc77cedcb4",
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=7d92dea45d94be7e1d4e427c728d335d",
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=e6e53eef152bba01a4c666a4d8b01117",
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da"
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=6cf460bed48ab738041f60231a3f005a",
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=9f944e8021781af1ce45d27765d1c0c2",
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=d409d9b1a3b69247df8b98941ba06e33",
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460"
}

View file

@ -399,7 +399,7 @@ img.navbar-brand-img, .navbar-brand>img {
}
.icon-med {
font-size: 20px;
font-size: 14px;
color: #889195;
}

View file

@ -124,11 +124,11 @@ a {
--button-primary: darken(@black, 25%);
--button-hover: darken(@black, 30%);
--header: @black; /* Use same as Header picker */
--text-main: #BBB;
--text-main: #fff;
--text-sub: #9b9b9b;
--link: #AAA; /* Use same as Header picker, lighten by 70% */
--visited-link: lighten(@black, 40%); /* Use same as Header picker, lighten by 70% */
--hover-link: lighten(@black, 45%); /* Use same as Header picker, lighten by 70% */
--link: #fff; /* Use same as Header picker, lighten by 70% */
--visited-link: #fff; /* Use same as Header picker, lighten by 70% */
--hover-link: #949494; /* Use same as Header picker, lighten by 70% */
--nav-link: #FFF; /* Use same as Header picker */
--light-link: #fff; /* Use same as Header picker */
}
@ -193,18 +193,6 @@ h2.task_menu{
color: var(--text-main);
}
a:link {
color: var(--link);
}
a:visited {
color: var(--nav-link);
}
a:hover {
color: var(--hover-link);
}
.btn-primary.hover {
color: var(--nav-link);
}
@ -318,8 +306,11 @@ input[type=text], input[type=search] {
background-color: var(--back-sub);
color: var(--text-main);
}
.search-highlight, .search-highlight:hover{
background-color: var(--back-sub) !important;
}
.input-group, .input-group-addon {
background-color: var(--back-sub)!important;
background-color: var(--back-sub);
color: var(--text-main);
}
#licensesTable>tbody>tr>td>nobr>a>i.fa {
@ -363,11 +354,11 @@ input[type=text], input[type=search] {
}
.select2-container--default .select2-results__option[aria-selected=true], .select2-container--default .select2-results__option[aria-selected=true]:hover {
background-color: var(--back-sub);
color: var(--header);
color: var(--nav-link);
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: var(--header);
color: var(--back-main);
background-color: var(--back-sub);
color: var(--visited-link);
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: var(--text-main);
@ -426,9 +417,6 @@ a {
color: var(--hover-link);
text-decoration: underline;
}
&:visited {
color: var(--visited-link)
}
}
.row-striped {
@ -463,4 +451,7 @@ a {
}
div.container.row-new-striped{
background-color: var(--back-sub);
}
.table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th {
background-color: var(--back-sub);
}

Some files were not shown because too many files have changed in this diff Show more