Compare commits

..

3 Commits

Author SHA1 Message Date
snipe 8052447de6 Ugh
Signed-off-by: snipe <snipe@snipe.net>
2024-10-03 14:34:29 +01:00
snipe b4f9284d41 Added migration
Signed-off-by: snipe <snipe@snipe.net>
2024-10-02 21:34:31 +01:00
snipe aac876eaa8 First stab
Signed-off-by: snipe <snipe@snipe.net>
2024-10-02 21:30:08 +01:00
170 changed files with 2969 additions and 7031 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ API_TOKEN_EXPIRATION_YEARS=40
# --------------------------------------------
# OPTIONAL: SECURITY HEADER SETTINGS
# --------------------------------------------
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.16.0.0/12
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.0.0.0/8
ALLOW_IFRAMING=false
REFERRER_POLICY=same-origin
ENABLE_CSP=false
+1 -1
View File
@@ -76,4 +76,4 @@ jobs:
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
run: php artisan test
run: php artisan test --parallel
+1 -1
View File
@@ -74,4 +74,4 @@ jobs:
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
run: php artisan test
run: php artisan test --parallel
+1 -1
View File
@@ -58,4 +58,4 @@ jobs:
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite_testing
run: php artisan test
run: php artisan test --parallel
+1 -5
View File
@@ -84,11 +84,7 @@ Since the release of the JSON REST API, several third-party developers have been
### Contributing
Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them.
Ideally, contributions should follow from a human-to-human discussion in the form of an issue.
Please see the complete documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
+97 -102
View File
@@ -137,24 +137,23 @@ class LdapSync extends Command
}
/* Determine which location to assign users to by default. */
$default_location = null;
$location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose
if ($this->option('location') != '') {
if ($default_location = Location::where('name', '=', $this->option('location'))->first()) {
if ($location = Location::where('name', '=', $this->option('location'))->first()) {
Log::debug('Location name ' . $this->option('location') . ' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
}
} elseif ($this->option('location_id')) {
//TODO - figure out how or why this is an array?
foreach($this->option('location_id') as $location_id) {
if ($default_location = Location::where('id', '=', $location_id)->first()) {
if ($location = Location::where('id', '=', $location_id)->first()) {
Log::debug('Location ID ' . $location_id . ' passed');
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
}
}
}
if (!isset($default_location)) {
if (! isset($location)) {
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
@@ -230,44 +229,43 @@ class LdapSync extends Command
for ($i = 0; $i < $results['count']; $i++) {
$item = [];
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? '';
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
$item['location_id'] = $results[$i]['location_id'] ?? '';
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
$location = $default_location; //initially, set '$location' to the default_location (which may just be `null`)
$item = [];
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? '';
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
$item['location_id'] = $results[$i]['location_id'] ?? '';
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
if ($ldap_map["location"] && $item['location']) {
$location = Location::firstOrCreate([
'name' => $item['location'],
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
if ($ldap_map["location"] && $item['location']) {
$location = Location::firstOrCreate([
'name' => $item['location'],
]);
}
$department = Department::firstOrCreate([
'name' => $item['department'],
]);
}
$department = Department::firstOrCreate([
'name' => $item['department'],
]);
$user = User::where('username', $item['username'])->first();
if ($user) {
// Updating an existing user.
$item['createorupdate'] = 'updated';
} else {
// Creating a new user.
$user = new User;
$user->password = $user->noPassword();
$user->locale = app()->getLocale();
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
$item['createorupdate'] = 'created';
}
$user = User::where('username', $item['username'])->first();
if ($user) {
// Updating an existing user.
$item['createorupdate'] = 'updated';
} else {
// Creating a new user.
$user = new User;
$user->password = $user->noPassword();
$user->locale = app()->getLocale();
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
$item['createorupdate'] = 'created';
}
//If a sync option is not filled in on the LDAP settings don't populate the user field
if($ldap_map["username"] != null){
@@ -298,7 +296,7 @@ class LdapSync extends Command
$user->department_id = $department->id;
}
if($ldap_map["location"] != null){
$user->location_id = $location?->id;
$user->location_id = $location ? $location->id : null;
}
if($ldap_map["manager"] != null){
@@ -343,38 +341,38 @@ class LdapSync extends Command
}
}
// Sync activated state for Active Directory.
if (!empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set....
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
// (Specifically, we don't handle a value of '0.0' correctly)
$raw_value = @$results[$i][$ldap_map["active_flag"]][0];
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$boolean_cast = (bool) $raw_value;
// Sync activated state for Active Directory.
if ( !empty($ldap_map["active_flag"])) { // IF we have an 'active' flag set....
// ....then *most* things that are truthy will activate the user. Anything falsey will deactivate them.
// (Specifically, we don't handle a value of '0.0' correctly)
$raw_value = @$results[$i][$ldap_map["active_flag"]][0];
$filter_var = filter_var($raw_value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
$boolean_cast = (bool)$raw_value;
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
$user->activated = $filter_var ?? $boolean_cast; // if filter_var() was true or false, use that. If it's null, use the $boolean_cast
} elseif (array_key_exists('useraccountcontrol', $results[$i])) {
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
} elseif (array_key_exists('useraccountcontrol', $results[$i]) ) {
// ....otherwise, (ie if no 'active' LDAP flag is defined), IF the UAC setting exists,
// ....then use the UAC setting on the account to determine can-log-in vs. cannot-log-in
/* The following is _probably_ the correct logic, but we can't use it because
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
/* The following is _probably_ the correct logic, but we can't use it because
some users may have been dependent upon the previous behavior, and this
could cause additional access to be available to users they don't want
to allow to log in.
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
$useraccountcontrol = $results[$i]['useraccountcontrol'][0];
if(
// based on MS docs at: https://support.microsoft.com/en-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties
($useraccountcontrol & 0x200) && // is a NORMAL_ACCOUNT
!($useraccountcontrol & 0x02) && // *and* _not_ ACCOUNTDISABLE
!($useraccountcontrol & 0x10) // *and* _not_ LOCKOUT
) {
$user->activated = 1;
} else {
$user->activated = 0;
} */
$enabled_accounts = [
'512', // 0x200 NORMAL_ACCOUNT
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
@@ -387,47 +385,44 @@ class LdapSync extends Command
'4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
'1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
'1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED,
];
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
];
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
// If we're not using AD, and there isn't an activated flag set, activate all users
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
already-existing accounts will be however the administrator has set them */
} /* implied 'else' here - leave the $user->activated flag alone. Newly-created accounts will be active.
already-existing accounts will be however the administrator has set them */
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (!empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id; //THIS is the magic line, this should do it.
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (! empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
}
// TODO - should we be NULLING locations if $location is really `null`, and that's what we came up with?
// will that conflict with any overriding setting that the user set? Like, if they moved someone from
// the 'null' location to somewhere, we wouldn't want to try to override that, right?
$location = null;
$user->ldap_import = 1;
$location = null;
$user->ldap_import = 1;
$errors = '';
$errors = '';
if ($user->save()) {
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
$user->groups()->attach($ldap_default_group);
if ($user->save()) {
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ( $item['createorupdate'] === 'created' && $ldap_default_group) {
$user->groups()->attach($ldap_default_group);
}
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
}
$item['note'] = $errors;
$item['status'] = 'error';
}
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
}
$item['note'] = $errors;
$item['status'] = 'error';
}
array_push($summary, $item);
array_push($summary, $item);
}
if ($this->option('summary')) {
+32 -17
View File
@@ -6,7 +6,6 @@ use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Helper\ProgressIndicator;
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
@@ -30,11 +29,6 @@ class ObjectImportCommand extends Command
*/
protected $description = 'Import Items from CSV';
/**
* The progress indicator instance.
*/
protected ProgressIndicator $progressIndicator;
/**
* Create a new command instance.
*
@@ -45,6 +39,8 @@ class ObjectImportCommand extends Command
parent::__construct();
}
private $bar;
/**
* Execute the console command.
*
@@ -52,8 +48,6 @@ class ObjectImportCommand extends Command
*/
public function handle()
{
$this->progressIndicator = new ProgressIndicator($this->output);
$filename = $this->argument('filename');
$class = title_case($this->option('item-type'));
$classString = "App\\Importer\\{$class}Importer";
@@ -67,25 +61,46 @@ class ObjectImportCommand extends Command
// This $logFile/useFiles() bit is currently broken, so commenting it out for now
// $logFile = $this->option('logfile');
// Log::useFiles($logFile);
$this->progressIndicator->start('======= Importing Items from '.$filename.' =========');
$this->comment('======= Importing Items from '.$filename.' =========');
$importer->import();
$this->progressIndicator->finish('Import finished.');
$this->bar = null;
if (! empty($this->errors)) {
$this->comment('The following Errors were encountered.');
foreach ($this->errors as $asset => $error) {
$this->comment('Error: Item: '.$asset.' failed validation: '.json_encode($error));
}
} else {
$this->comment('All Items imported successfully!');
}
$this->comment('');
}
public function errorCallback($item, $field, $error)
public function errorCallback($item, $field, $errorString)
{
$this->output->write("\x0D\x1B[2K");
$this->warn('Error: Item: '.$item->name.' failed validation: '.json_encode($error));
$this->errors[$item->name][$field] = $errorString;
}
public function progress($importedItemsCount)
public function progress($count)
{
$this->progressIndicator->advance();
if (! $this->bar) {
$this->bar = $this->output->createProgressBar($count);
}
static $index = 0;
$index++;
if ($index < $count) {
$this->bar->advance();
} else {
$this->bar->finish();
}
}
// Tracks the current item for error messages
private $updating;
// An array of errors encountered while parsing
private $errors;
/**
* Log a message to file, configurable by the --log-file parameter.
* If a warning message is passed, we'll spit it to the console as well.
+38 -5
View File
@@ -553,7 +553,7 @@ class Helper
*/
public static function statusLabelList()
{
$statuslabel_list = ['' => trans('general.select_statuslabel')] + Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('status_type', 'desc')
$statuslabel_list = ['' => trans('general.select_statuslabel')] + Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('deployable', 'desc')
->pluck('name', 'id')->toArray();
return $statuslabel_list;
@@ -572,9 +572,9 @@ class Helper
*/
public static function deployableStatusLabelList()
{
$statuslabel_list = Statuslabel::where('status_type', 'deployable')->orderBy('default_label', 'desc')
$statuslabel_list = Statuslabel::where('deployable', '=', '1')->orderBy('default_label', 'desc')
->orderBy('name', 'asc')
->orderBy('status_type', 'desc')
->orderBy('deployable', 'desc')
->pluck('name', 'id')->toArray();
return $statuslabel_list;
@@ -1123,7 +1123,6 @@ class Helper
'png' => 'far fa-image',
'webp' => 'far fa-image',
'avif' => 'far fa-image',
'svg' => 'fas fa-vector-square',
// word
'doc' => 'far fa-file-word',
'docx' => 'far fa-file-word',
@@ -1136,7 +1135,7 @@ class Helper
//Text
'txt' => 'far fa-file-alt',
'rtf' => 'far fa-file-alt',
'xml' => 'fas fa-code',
'xml' => 'far fa-file-alt',
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
@@ -1149,7 +1148,41 @@ class Helper
return 'far fa-file';
}
public static function show_file_inline($filename)
{
$extension = substr(strrchr($filename, '.'), 1);
if ($extension) {
switch ($extension) {
case 'jpg':
case 'jpeg':
case 'gif':
case 'png':
case 'webp':
case 'avif':
return true;
break;
default:
return false;
}
}
return false;
}
/**
* Generate a random encrypted password.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
public static function generateEncyrptedPassword(): string
{
return bcrypt(self::generateUnencryptedPassword());
}
/**
* Get a random unencrypted password.
-61
View File
@@ -7,7 +7,6 @@ use Illuminate\Http\Response;
use Illuminate\Http\RedirectResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
class StorageHelper
{
public static function downloader($filename, $disk = 'default') : BinaryFileResponse | RedirectResponse | StreamedResponse
@@ -26,64 +25,4 @@ class StorageHelper
return Storage::disk($disk)->download($filename);
}
}
/**
* This determines the file types that should be allowed inline and checks their fileinfo extension
* to determine that they are safe to display inline.
*
* @author <A. Gianotto> [<snipe@snipe.net]>
* @since v7.0.14
* @param $file_with_path
* @return bool
*/
public static function allowSafeInline($file_with_path) {
$allowed_inline = [
'pdf',
'svg',
'jpg',
'gif',
'svg',
'avif',
'webp',
'png',
];
// The file exists and is allowed to be displayed inline
if (Storage::exists($file_with_path) && (in_array(pathinfo($file_with_path, PATHINFO_EXTENSION), $allowed_inline))) {
return true;
}
return false;
}
/**
* Decide whether to show the file inline or download it.
*/
public static function showOrDownloadFile($file, $filename) {
$headers = [];
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
// This is NOT allowed as inline - force it to be displayed as text in the browser
if (self::allowSafeInline($file) != true) {
$headers = array_merge($headers, ['Content-Type' => 'text/plain']);
}
}
// Everything else seems okay, but the file doesn't exist on the server.
if (Storage::missing($file)) {
throw new FileNotFoundException();
}
return Storage::download($file, $filename, $headers);
}
}
@@ -106,29 +106,50 @@ class AccessoriesFilesController extends Controller
* @param int $accessoryId
* @param int $fileId
*/
public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
public function show($accessoryId = null, $fileId = null, $download = true) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
{
Log::debug('Private filesystem is: '.config('filesystems.default'));
$accessory = Accessory::find($accessoryId);
// the accessory is valid
if ($accessory = Accessory::find($accessoryId)) {
if (isset($accessory->id)) {
$this->authorize('view', $accessory);
$this->authorize('accessories.files', $accessory);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
$file = 'private_uploads/accessories/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
}
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
return redirect()->route('accessories.index')->with('error', trans('admin/users/message.log_record_not_found'));
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.log_record_not_found'));
$file = 'private_uploads/accessories/'.$log->filename;
if (Storage::missing($file)) {
Log::debug('FILE DOES NOT EXISTS for '.$file);
Log::debug('URL should be '.Storage::url($file));
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
->header('Content-Type', 'text/plain');
} else {
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
// We have to override the URL stuff here, since local defaults in Laravel's Flysystem
// won't work, as they're not accessible via the web
if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
return StorageHelper::downloader($file);
}
}
}
return redirect()->route('accessories.index')->with('error', trans('general.file_not_found'));
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
}
}
@@ -137,6 +137,7 @@ class AccessoriesController extends Controller
*/
public function store(StoreAccessoryRequest $request)
{
$this->authorize('create', Accessory::class);
$accessory = new Accessory;
$accessory->fill($request->all());
$accessory = $request->handleImages($accessory);
@@ -196,6 +197,9 @@ class AccessoriesController extends Controller
$this->authorize('view', Accessory::class);
$accessory = Accessory::with('lastCheckout')->findOrFail($id);
if (! Company::isCurrentUserHasAccess($accessory)) {
return ['total' => 0, 'rows' => []];
}
$offset = request('offset', 0);
$limit = request('limit', 50);
@@ -321,7 +325,7 @@ class AccessoriesController extends Controller
$accessory = Accessory::find($accessory_checkout->accessory_id);
$this->authorize('checkin', $accessory);
$accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
// Was the accessory updated?
if ($accessory_checkout->delete()) {
@@ -329,6 +333,14 @@ class AccessoriesController extends Controller
$user = User::find($accessory_checkout->assigned_to);
}
$data['log_id'] = $logaction->id;
$data['first_name'] = $user->first_name;
$data['last_name'] = $user->last_name;
$data['item_name'] = $accessory->name;
$data['checkin_date'] = $logaction->created_at;
$data['item_tag'] = '';
$data['note'] = $logaction->note;
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkin.success')));
}
+22 -14
View File
@@ -61,7 +61,7 @@ class AssetsController extends Controller
if ($action == 'audit') {
$action = 'audits';
}
$filter_non_depreciable_assets = false;
$filter_non_deprecable_assets = false;
/**
* This looks MAD janky (and it is), but the AssetsController@index does a LOT of heavy lifting throughout the
@@ -75,7 +75,7 @@ class AssetsController extends Controller
* which would have been far worse of a mess. *sad face* - snipe (Sept 1, 2021)
*/
if (Route::currentRouteName()=='api.depreciation-report.index') {
$filter_non_depreciable_assets = true;
$filter_non_deprecable_assets = true;
$transformer = 'App\Http\Transformers\DepreciationReportTransformer';
$this->authorize('reports.view');
} else {
@@ -130,9 +130,9 @@ class AssetsController extends Controller
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
if ($filter_non_depreciable_assets) {
$non_depreciable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
$assets->InModelList($non_depreciable_models->toArray());
if ($filter_non_deprecable_assets) {
$non_deprecable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
$assets->InModelList($non_deprecable_models->toArray());
}
@@ -206,14 +206,18 @@ class AssetsController extends Controller
case 'Pending':
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.status_type', '=', 'pending');
->where('status_alias.deployable', '=', 0)
->where('status_alias.pending', '=', 1)
->where('status_alias.archived', '=', 0);
});
break;
case 'RTD':
$assets->whereNull('assets.assigned_to')
->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.status_type', '=', 'deployable');
->where('status_alias.deployable', '=', 1)
->where('status_alias.pending', '=', 0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Undeployable':
@@ -222,15 +226,20 @@ class AssetsController extends Controller
case 'Archived':
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.status_type', '=', 'archived');
->where('status_alias.deployable', '=', 0)
->where('status_alias.pending', '=', 0)
->where('status_alias.archived', '=', 1);
});
break;
case 'Requestable':
$assets->where('assets.requestable', '=', 1)
->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.status_type', '=', 'deployable');
->where('status_alias.deployable', '=', 1)
->where('status_alias.pending', '=', 0)
->where('status_alias.archived', '=', 0);
});
break;
case 'Deployed':
// more sad, horrible workarounds for laravel bugs when doing full text searches
@@ -247,7 +256,7 @@ class AssetsController extends Controller
// terrible workaround for complex-query Laravel bug in fulltext
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.status_type', '!=', 'archived');
->where('status_alias.archived', '=', 0);
});
// If there is a status ID, don't take show_archived_in_list into consideration
@@ -256,7 +265,6 @@ class AssetsController extends Controller
$join->on('status_alias.id', '=', 'assets.status_id');
});
}
break;
}
@@ -387,7 +395,7 @@ class AssetsController extends Controller
// This may not work for all databases, but it works for MySQL
if ($numeric_sort) {
$assets->orderByRaw(DB::getTablePrefix() . 'assets.' . $sort_override . ' * 1 ' . $order);
$assets->orderByRaw($sort_override . ' * 1 ' . $order);
} else {
$assets->orderBy($sort_override, $order);
}
@@ -566,8 +574,8 @@ class AssetsController extends Controller
}
if ($asset->assetstatus->status_label == 'pending') {
$asset->use_text .= '('.$asset->assetstatus->status_label.')';
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
}
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
@@ -38,7 +38,6 @@ class ComponentsController extends Controller
'name',
'min_amt',
'order_number',
'model_number',
'serial',
'purchase_date',
'purchase_cost',
@@ -48,7 +47,7 @@ class ComponentsController extends Controller
];
$components = Component::select('components.*')
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer');
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser');
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
@@ -70,14 +69,6 @@ class ComponentsController extends Controller
$components->where('supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('manufacturer_id')) {
$components->where('manufacturer_id', '=', $request->input('manufacturer_id'));
}
if ($request->filled('model_number')) {
$components->where('model_number', '=', $request->input('model_number'));
}
if ($request->filled('location_id')) {
$components->where('location_id', '=', $request->input('location_id'));
}
@@ -107,9 +98,6 @@ class ComponentsController extends Controller
case 'supplier':
$components = $components->OrderSupplier($order);
break;
case 'manufacturer':
$components = $components->OrderManufacturer($order);
break;
case 'created_by':
$components = $components->OrderByCreatedBy($order);
break;
@@ -258,8 +258,6 @@ class ConsumablesController extends Controller
$this->authorize('checkout', $consumable);
$consumable->checkout_qty = $request->input('checkout_qty', 1);
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable')));
@@ -270,12 +268,6 @@ class ConsumablesController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')])));
}
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0 || $consumable->checkout_qty > $consumable->numRemaining()) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable', ['requested' => $consumable->checkout_qty, 'remaining' => $consumable->numRemaining() ])));
}
// Check if the user exists - @TODO: this should probably be handled via validation, not here??
if (!$user = User::find($request->input('assigned_to'))) {
@@ -286,8 +278,7 @@ class ConsumablesController extends Controller
// Update the consumable data
$consumable->assigned_to = $request->input('assigned_to');
for ($i = 0; $i < $consumable->checkout_qty; $i++) {
$consumable->users()->attach($consumable->id,
$consumable->users()->attach($consumable->id,
[
'consumable_id' => $consumable->id,
'created_by' => $user->id,
@@ -295,8 +286,6 @@ class ConsumablesController extends Controller
'note' => $request->input('note'),
]
);
}
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
@@ -65,7 +65,7 @@ class ImportController extends Controller
ini_set('auto_detect_line_endings', '1');
}
$reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak?
$reader->setDelimiter(request('delimiter', ','));
try {
$import->header_row = $reader->fetchOne(0);
} catch (JsonEncodingException $e) {
@@ -133,6 +133,7 @@ class ImportController extends Controller
}
$import->filesize = filesize($path.'/'.$file_name);
$import->delimiter = request('delimiter');
$import->save();
$results[] = $import;
@@ -60,8 +60,7 @@ class ManufacturersController extends Controller
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('consumables as consumables_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count');
->withCount('accessories as accessories_count');
if ($request->input('deleted') == 'true') {
$manufacturers->onlyTrashed();
@@ -32,8 +32,7 @@ class StatuslabelsController extends Controller
'assets_count',
'color',
'notes',
'default_label',
'status_type',
'default_label'
];
$statuslabels = Statuslabel::with('adminuser')->withCount('assets as assets_count');
@@ -50,12 +49,13 @@ class StatuslabelsController extends Controller
// if a status_type is passed, filter by that
if ($request->filled('status_type')) {
if (strtolower($request->input('status_type')) == 'pending') {
$statuslabels->where('status_type', '=', 'pending');
} elseif (strtolower($request->input('status_type')) == 'archived') $statuslabels->where('status_type', '=', 'archived');
elseif (strtolower($request->input('status_type')) == 'deployable') {
$statuslabels->where('status_type', '=', 'deployable');
$statuslabels = $statuslabels->Pending();
} elseif (strtolower($request->input('status_type')) == 'archived') {
$statuslabels = $statuslabels->Archived();
} elseif (strtolower($request->input('status_type')) == 'deployable') {
$statuslabels = $statuslabels->Deployable();
} elseif (strtolower($request->input('status_type')) == 'undeployable') {
$statuslabels->whereNot('status_type', 'deployable');
$statuslabels = $statuslabels->Undeployable();
}
}
@@ -92,11 +92,19 @@ class StatuslabelsController extends Controller
public function store(Request $request) : JsonResponse
{
$this->authorize('create', Statuslabel::class);
$request->except('deployable', 'pending', 'archived');
if (! $request->filled('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, ['type' => ['Status label type is required.']]), 500);
}
$statuslabel = new Statuslabel;
$statuslabel->fill($request->all());
$statuslabel->status_type = $request->input('status_type');
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
$statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived'];
$statuslabel->color = $request->input('color');
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
$statuslabel->default_label = $request->input('default_label', 0);
@@ -137,12 +145,20 @@ class StatuslabelsController extends Controller
{
$this->authorize('update', Statuslabel::class);
$statuslabel = Statuslabel::findOrFail($id);
$request->except('deployable', 'pending', 'archived');
if (! $request->filled('type')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Status label type is required.'));
}
$statuslabel->fill($request->all());
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
$statuslabel->status_type = $request->input('status_type');
$statuslabel->deployable = $statusType['deployable'];
$statuslabel->pending = $statusType['pending'];
$statuslabel->archived = $statusType['archived'];
$statuslabel->color = $request->input('color');
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
$statuslabel->default_label = $request->input('default_label', 0);
@@ -190,7 +206,7 @@ class StatuslabelsController extends Controller
$this->authorize('view', Statuslabel::class);
if (Setting::getSettings()->show_archived_in_list == 0 ) {
$statuslabels = Statuslabel::withCount('assets')->whereNot('status_type','archived')->get();
$statuslabels = Statuslabel::withCount('assets')->where('archived','0')->get();
} else {
$statuslabels = Statuslabel::withCount('assets')->get();
}
@@ -284,7 +300,7 @@ class StatuslabelsController extends Controller
public function checkIfDeployable($id) : string
{
$statuslabel = Statuslabel::findOrFail($id);
if ($statuslabel->status_type == 'deployable') {
if ($statuslabel->getStatuslabelType() == 'deployable') {
return '1';
}
@@ -302,22 +318,22 @@ class StatuslabelsController extends Controller
{
$this->authorize('view.selectlists');
$statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('status_type', 'desc');
$statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('deployable', 'desc');
if ($request->filled('search')) {
$statuslabels = $statuslabels->where('name', 'LIKE', '%'.$request->get('search').'%');
}
if ($request->filled('deployable')) {
$statuslabels = $statuslabels->where('status_type', '=', 'deployable');
$statuslabels = $statuslabels->where('deployable', '=', '1');
}
if ($request->filled('pending')) {
$statuslabels = $statuslabels->where('status_type', '=', 'pending');
$statuslabels = $statuslabels->where('pending', '=', '1');
}
if ($request->filled('archived')) {
$statuslabels = $statuslabels->where('status_type', '=', 'archived');
$statuslabels = $statuslabels->where('archived', '=', '1');
}
$statuslabels = $statuslabels->orderBy('name', 'ASC')->paginate(50);
+2 -9
View File
@@ -14,7 +14,6 @@ use App\Http\Transformers\UsersTransformer;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\License;
use App\Models\User;
@@ -43,14 +42,13 @@ class UsersController extends Controller
$users = User::select([
'users.activated',
'users.created_by',
'users.address',
'users.avatar',
'users.city',
'users.company_id',
'users.country',
'users.created_by',
'users.created_at',
'users.updated_at',
'users.deleted_at',
'users.department_id',
'users.email',
@@ -69,6 +67,7 @@ class UsersController extends Controller
'users.state',
'users.two_factor_enrolled',
'users.two_factor_optin',
'users.updated_at',
'users.username',
'users.zip',
'users.remote',
@@ -256,7 +255,6 @@ class UsersController extends Controller
'groups',
'activated',
'created_at',
'updated_at',
'two_factor_enrolled',
'two_factor_optin',
'last_login',
@@ -372,7 +370,6 @@ class UsersController extends Controller
$user = new User;
$user->fill($request->all());
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$user->created_by = auth()->id();
if ($request->has('permissions')) {
@@ -454,10 +451,6 @@ class UsersController extends Controller
$user->fill($request->all());
if ($request->filled('company_id')) {
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
}
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
@@ -61,30 +61,43 @@ class AssetFilesController extends Controller
*/
public function show($assetId = null, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
{
if ($asset = Asset::find($assetId)) {
$asset = Asset::find($assetId);
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
$file = 'private_uploads/assets/'.$log->filename;
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.file_not_found'));
}
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found'));
$file = 'private_uploads/assets/'.$log->filename;
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
if (! Storage::exists($file)) {
return response('File '.$file.' not found on server', 404)
->header('Content-Type', 'text/plain');
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Prepare the error message
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
// Redirect to the hardware management page
return redirect()->route('hardware.index')->with('error', $error);
}
/**
@@ -17,6 +17,7 @@ use App\Models\Location;
use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\View\Label;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
@@ -111,10 +112,8 @@ class AssetsController extends Controller
$settings = Setting::getSettings();
$successes = [];
$failures = [];
$success = false;
$serials = $request->input('serials');
$asset = null;
for ($a = 1; $a <= count($asset_tags); $a++) {
$asset = new Asset();
@@ -201,35 +200,20 @@ class AssetsController extends Controller
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
}
$successes[] = "<a href='" . route('hardware.show', ['hardware' => $asset->id]) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
} else {
$failures[] = join(",", $asset->getErrors()->all());
$success = true;
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($successes) {
if ($failures) {
//some succeeded, some failed
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
} else {
if (count($successes) == 1) {
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
//and re-translated
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)]));
} else {
//multi-success
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
}
}
if ($success) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)]));
}
return redirect()->back()->withInput()->withErrors($asset->getErrors());
@@ -352,7 +336,7 @@ class AssetsController extends Controller
$status = Statuslabel::find($request->input('status_id'));
// This is a non-deployable status label - we should check the asset back in.
if (($status && $status->status_type != 'deployable') && ($target = $asset->assignedTo)) {
if (($status && $status->getStatuslabelType() != 'deployable') && ($target = $asset->assignedTo)) {
$originalValues = $asset->getRawOriginal();
$asset->assigned_to = null;
@@ -52,10 +52,6 @@ class BulkAssetsController extends Controller
}
$asset_ids = $request->input('ids');
if ($request->input('bulk_actions') === 'checkout') {
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('hardware.bulkcheckout.show');
}
// Figure out where we need to send the user after the update is complete, and store that in the session
$bulk_back_url = request()->headers->get('referer');
@@ -575,34 +571,31 @@ class BulkAssetsController extends Controller
}
$errors = [];
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference!
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
foreach ($asset_ids as $asset_id) {
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkout', $asset);
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
//TODO - I think this logic is duplicated in the checkOut method?
if ($target->location_id != '') {
$asset->location_id = $target->location_id;
// TODO - I don't know why this is being saved without events
$asset::withoutEvents(function () use ($asset) {
$asset->save();
});
$asset->unsetEventDispatcher();
$asset->save();
}
if (!$checkout_success) {
$errors = array_merge_recursive($errors, $asset->getErrors()->toArray());
if ($error) {
array_merge_recursive($errors, $asset->getErrors()->toArray());
}
}
});
if (! $errors) {
// Redirect to the new asset page
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors);
return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
} catch (ModelNotFoundException $e) {
return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors());
}
@@ -73,8 +73,6 @@ class ComponentsController extends Controller
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->manufacturer_id = $request->input('manufacturer_id');
$component->model_number = $request->input('model_number');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number', null);
@@ -152,8 +150,6 @@ class ComponentsController extends Controller
$component->name = $request->input('name');
$component->category_id = $request->input('category_id');
$component->supplier_id = $request->input('supplier_id');
$component->manufacturer_id = $request->input('manufacturer_id');
$component->model_number = $request->input('model_number');
$component->location_id = $request->input('location_id');
$component->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$component->order_number = $request->input('order_number');
@@ -112,25 +112,40 @@ class ComponentsFilesController extends Controller
public function show($componentId = null, $fileId = null)
{
Log::debug('Private filesystem is: '.config('filesystems.default'));
$component = Component::find($componentId);
// the component is valid
if ($component = Component::find($componentId)) {
if (isset($component->id)) {
$this->authorize('view', $component);
$this->authorize('components.files', $component);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
$file = 'private_uploads/components/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found'));
}
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found'));
$file = 'private_uploads/components/'.$log->filename;
if (Storage::missing($file)) {
Log::debug('FILE DOES NOT EXISTS for '.$file);
Log::debug('URL should be '.Storage::url($file));
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
->header('Content-Type', 'text/plain');
} else {
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
return StorageHelper::downloader($file);
}
}
}
return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
@@ -70,7 +70,7 @@ class ConsumableCheckoutController extends Controller
$this->authorize('checkout', $consumable);
// If the quantity is not present in the request or is not a positive integer, set it to 1
$quantity = $request->input('checkout_qty');
$quantity = $request->input('qty');
if (!isset($quantity) || !ctype_digit((string)$quantity) || $quantity <= 0) {
$quantity = 1;
}
@@ -92,7 +92,7 @@ class ConsumableCheckoutController extends Controller
// Update the consumable data
$consumable->assigned_to = e($request->input('assigned_to'));
for ($i = 0; $i < $quantity; $i++){
for($i = 0; $i < $quantity; $i++){
$consumable->users()->attach($consumable->id, [
'consumable_id' => $consumable->id,
'created_by' => $admin_user->id,
@@ -100,8 +100,6 @@ class ConsumableCheckoutController extends Controller
'note' => $request->input('note'),
]);
}
$consumable->checkout_qty = $quantity;
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
$request->request->add(['checkout_to_type' => 'user']);
@@ -104,6 +104,7 @@ class ConsumablesFilesController extends Controller
* @since [v1.4]
* @param int $consumableId
* @param int $fileId
* @return \Symfony\Consumable\HttpFoundation\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($consumableId = null, $fileId = null)
@@ -115,18 +116,36 @@ class ConsumablesFilesController extends Controller
$this->authorize('view', $consumable);
$this->authorize('consumables.files', $consumable);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
$file = 'private_uploads/consumables/'.$log->filename;
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found'));
$file = 'private_uploads/consumables/'.$log->filename;
if (Storage::missing($file)) {
Log::debug('FILE DOES NOT EXISTS for '.$file);
Log::debug('URL should be '.Storage::url($file));
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
->header('Content-Type', 'text/plain');
} else {
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
// We have to override the URL stuff here, since local defaults in Laravel's Flysystem
// won't work, as they're not accessible via the web
if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
return StorageHelper::downloader($file);
}
}
// The log record doesn't exist somehow
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
+3 -26
View File
@@ -3,7 +3,6 @@
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\DB;
/**
* This controller provide the health route for
@@ -16,35 +15,13 @@ use Illuminate\Support\Facades\DB;
*/
class HealthController extends BaseController
{
public function __construct()
{
$this->middleware('health');
}
/**
* Returns a fixed JSON content ({ "status": "ok"}) which indicate the app is up and running
*/
public function get()
{
try {
if (DB::select('select 2 + 2')) {
return response()->json([
'status' => 'ok',
]);
}
} catch (\Exception $e) {
\Log::error('Could not connect to database');
return response()->json([
'status' => 'database connection failed',
], 500);
}
return response()->json([
'status' => 'ok',
]);
}
}
@@ -112,19 +112,37 @@ class LicenseFilesController extends Controller
$this->authorize('view', $license);
$this->authorize('licenses.files', $license);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
$file = 'private_uploads/licenses/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.file_not_found'));
}
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
return response('No matching record for that asset/file', 500)
->header('Content-Type', 'text/plain');
}
// The log record doesn't exist somehow
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found'));
$file = 'private_uploads/licenses/'.$log->filename;
if (Storage::missing($file)) {
Log::debug('NOT EXISTS for '.$file);
Log::debug('NOT EXISTS URL should be '.Storage::url($file));
return response('File '.$file.' ('.Storage::url($file).') not found on server', 404)
->header('Content-Type', 'text/plain');
} else {
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
// We have to override the URL stuff here, since local defaults in Laravel's Flysystem
// won't work, as they're not accessible via the web
if (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
return StorageHelper::downloader($file);
}
}
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId]));
+1 -1
View File
@@ -40,7 +40,7 @@ class ModalController extends Controller
$view = view("modals.${type}");
if ($type == "statuslabel") {
$view->with('status_types', Helper::statusTypeList());
$view->with('statuslabel_types', Helper::statusTypeList());
}
if (in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) {
$view->with('kitId', $itemId);
+7 -12
View File
@@ -194,14 +194,14 @@ class ProfileController extends Controller
*/
public function printInventory() : View
{
$show_users = User::where('id',auth()->user()->id)->get();
$show_user = auth()->user();
return view('users/print')
->with('assets', auth()->user()->assets())
->with('licenses', auth()->user()->licenses()->get())
->with('accessories', auth()->user()->accessories()->get())
->with('consumables', auth()->user()->consumables()->get())
->with('users', $show_users)
->with('assets', auth()->user()->assets)
->with('licenses', $show_user->licenses()->get())
->with('accessories', $show_user->accessories()->get())
->with('consumables', $show_user->consumables()->get())
->with('show_user', $show_user)
->with('settings', Setting::getSettings());
}
@@ -222,12 +222,7 @@ class ProfileController extends Controller
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
}
try {
$user->notify((new CurrentInventory($user)));
} catch (\Exception $e) {
\Log::error($e);
}
$user->notify((new CurrentInventory($user)));
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
}
}
+39 -11
View File
@@ -7,11 +7,6 @@ use App\Helpers\StorageHelper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SettingsSamlRequest;
use App\Http\Requests\SetupUserRequest;
use App\Http\Requests\StoreLdapSettings;
use App\Http\Requests\StoreLocalizationSettings;
use App\Http\Requests\StoreNotificationSettings;
use App\Http\Requests\StoreLabelSettings;
use App\Http\Requests\StoreSecuritySettings;
use App\Models\CustomField;
use App\Models\Group;
use App\Models\Setting;
@@ -278,6 +273,20 @@ class SettingsController extends Controller
return view('settings/index', compact('settings'));
}
/**
* Return the admin settings page.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
*
* @since [v1.0]
*/
public function getEdit() : View
{
$setting = Setting::getSettings();
return view('settings/general', compact('setting'));
}
/**
* Return a form to allow a super admin to update settings.
@@ -477,7 +486,7 @@ class SettingsController extends Controller
*
* @since [v1.0]
*/
public function postSecurity(StoreSecuritySettings $request) : RedirectResponse
public function postSecurity(Request $request) : RedirectResponse
{
$this->validate($request, [
'pwd_secure_complexity' => 'array',
@@ -547,7 +556,7 @@ class SettingsController extends Controller
*
* @since [v1.0]
*/
public function postLocalization(StoreLocalizationSettings $request) : RedirectResponse
public function postLocalization(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -590,7 +599,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
*/
public function postAlerts(StoreNotificationSettings $request) : RedirectResponse
public function postAlerts(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -771,7 +780,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function postLabels(StoreLabelSettings $request) : RedirectResponse
public function postLabels(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -850,7 +859,26 @@ class SettingsController extends Controller
{
$setting = Setting::getSettings();
$groups = Group::pluck('name', 'id');
return view('settings.ldap', compact('setting', 'groups'));
/**
* This validator is only temporary (famous last words.) - @snipe
*/
$messages = [
'ldap_username_field.not_in' => '<code>sAMAccountName</code> (mixed case) will likely not work. You should use <code>samaccountname</code> (lowercase) instead. ',
'ldap_auth_filter_query.not_in' => '<code>uid=samaccountname</code> is probably not a valid auth filter. You probably want <code>uid=</code> ',
'ldap_filter.regex' => 'This value should probably not be wrapped in parentheses.',
];
$validator = Validator::make($setting->toArray(), [
'ldap_username_field' => 'not_in:sAMAccountName',
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
], $messages);
return view('settings.ldap', compact('setting', 'groups'))->withErrors($validator);
}
/**
@@ -859,7 +887,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*/
public function postLdapSettings(StoreLdapSettings $request) : RedirectResponse
public function postLdapSettings(Request $request) : RedirectResponse
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
@@ -47,7 +47,7 @@ class StatuslabelsController extends Controller
return view('statuslabels/edit')
->with('item', new Statuslabel)
->with('status_types', Helper::statusTypeList());
->with('statuslabel_types', Helper::statusTypeList());
}
/**
@@ -61,11 +61,19 @@ class StatuslabelsController extends Controller
// create a new model instance
$statusLabel = new Statuslabel();
if ($request->missing('statuslabel_types')) {
return redirect()->back()->withInput()->withErrors(['statuslabel_types' => trans('validation.statuslabel_type')]);
}
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('statuslabel_types'));
// Save the Statuslabel data
$statusLabel->name = $request->input('name');
$statusLabel->created_by = auth()->id();
$statusLabel->notes = $request->input('notes');
$statusLabel->status_type = $request->input('status_type');
$statusLabel->deployable = $statusType['deployable'];
$statusLabel->pending = $statusType['pending'];
$statusLabel->archived = $statusType['archived'];
$statusLabel->color = $request->input('color');
$statusLabel->show_in_nav = $request->input('show_in_nav', 0);
$statusLabel->default_label = $request->input('default_label', 0);
@@ -92,7 +100,11 @@ class StatuslabelsController extends Controller
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
return view('statuslabels/edit', compact('item'))->with('status_types', Helper::statusTypeList());;
$use_statuslabel_type = $item->getStatuslabelType();
$statuslabel_types = ['' => trans('admin/hardware/form.select_statustype')] + ['undeployable' => trans('admin/hardware/general.undeployable')] + ['pending' => trans('admin/hardware/general.pending')] + ['archived' => trans('admin/hardware/general.archived')] + ['deployable' => trans('admin/hardware/general.deployable')];
return view('statuslabels/edit', compact('item', 'statuslabel_types'))->with('use_statuslabel_type', $use_statuslabel_type);
}
/**
@@ -109,10 +121,17 @@ class StatuslabelsController extends Controller
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
}
if (! $request->filled('statuslabel_types')) {
return redirect()->back()->withInput()->withErrors(['statuslabel_types' => trans('validation.statuslabel_type')]);
}
// Update the Statuslabel data
$statustype = Statuslabel::getStatuslabelTypesForDB($request->input('statuslabel_types'));
$statuslabel->name = $request->input('name');
$statuslabel->notes = $request->input('notes');
$statuslabel->status_type = $request->input('status_type');
$statuslabel->deployable = $statustype['deployable'];
$statuslabel->pending = $statustype['pending'];
$statuslabel->archived = $statustype['archived'];
$statuslabel->color = $request->input('color');
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
$statuslabel->default_label = $request->input('default_label', 0);
@@ -7,6 +7,9 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Illuminate\Support\Facades\Storage;
@@ -113,30 +116,31 @@ class UserFilesController extends Controller
public function show($userId = null, $fileId = null)
{
if (empty($fileId)) {
return redirect()->route('users.show')->with('error', 'Invalid file request');
}
if ($user = User::find($userId)) {
$user = User::find($userId);
// the license is valid
if (isset($user->id)) {
$this->authorize('view', $user);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
$file = 'private_uploads/users/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found'));
// Display the file inline
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
}
return Storage::download('private_uploads/users/'.$log->filename);
}
// The log record doesn't exist somehow
return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.log_record_not_found'));
return redirect()->back()->with('error', trans('general.file_not_found'));
return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found'));
}
// Redirect to the user management page if the user doesn't exist
-5
View File
@@ -53,10 +53,6 @@ class Kernel extends HttpKernel
\App\Http\Middleware\CheckLocale::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'health' => [
],
];
/**
@@ -73,6 +69,5 @@ class Kernel extends HttpKernel
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'health' => null,
];
}
+4 -9
View File
@@ -7,19 +7,14 @@ use Closure;
class CheckForSetup
{
protected $except = [
'_debugbar*',
'health'
];
public function handle($request, Closure $next, $guard = null)
{
/**
* Skip this middleware for the debugbar and health check
* This is dumb
* @todo Check on removing this, not sure if it's still needed
*/
if ($request->is($this->except)) {
if ($request->is('_debugbar*')) {
return $next($request);
}
@@ -30,7 +25,7 @@ class CheckForSetup
return $next($request);
}
} else {
if (! ($request->is('setup*')) && ! ($request->is('.env'))) {
if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) {
return redirect(config('app.url').'/setup');
}
+1 -2
View File
@@ -29,8 +29,7 @@ class StoreAssetRequest extends ImageUploadRequest
// Guard against users passing in an array for company_id instead of an integer.
// If the company_id is not an integer then we simply use what was
// provided to be caught by model level validation later.
// The use of is_numeric accounts for 1 and '1'.
$idForCurrentUser = is_numeric($this->company_id)
$idForCurrentUser = is_int($this->company_id)
? Company::getIdForCurrentUser($this->company_id)
: $this->company_id;
-41
View File
@@ -1,41 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreLabelSettings extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('superuser');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'labels_per_page' => 'numeric',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_pmargin_left' => 'numeric|nullable',
'labels_pmargin_right' => 'numeric|nullable',
'labels_pmargin_top' => 'numeric|nullable',
'labels_pmargin_bottom' => 'numeric|nullable',
'labels_display_bgutter' => 'numeric|nullable',
'labels_display_sgutter' => 'numeric|nullable',
'labels_fontsize' => 'numeric|min:5',
'labels_pagewidth' => 'numeric|nullable',
'labels_pageheight' => 'numeric|nullable',
'qr_text' => 'max:31|nullable',
];
}
}
-38
View File
@@ -1,38 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreLdapSettings extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('superuser');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'ldap_username_field' => 'not_in:sAMAccountName|required_if:ldap_enabled,1',
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://',
'ldap_uname' => 'nullable|required_if:ldap_enabled,1',
'ldap_pword' => 'nullable|required_if:ldap_enabled,1',
'ldap_basedn' => 'nullable|required_if:ldap_enabled,1',
'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1',
'custom_forgot_pass_url' => 'nullable|url',
];
}
}
@@ -1,30 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreLocalizationSettings extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('superuser');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'default_currency' => 'required',
'locale' => 'required',
];
}
}
@@ -1,37 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Models\Accessory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreNotificationSettings extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('superuser');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email|nullable',
'alert_threshold' => 'numeric|nullable|gt:0',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable|gt:0',
'due_checkin_days' => 'numeric|nullable|gt:0',
'audit_interval' => 'numeric|nullable|gt:0',
];
}
}
@@ -1,35 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreSecuritySettings extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('superuser');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'pwd_secure_min' => 'numeric|required|min:8',
'custom_forgot_pass_url' => 'url|nullable',
'privacy_policy_link' => 'nullable|url',
'login_remote_user_enabled' => 'numeric|nullable',
'login_common_disabled' => 'numeric|nullable',
'login_remote_user_custom_logout_url' => 'string|nullable',
'login_remote_user_header_name' => 'string|nullable',
];
}
}
@@ -23,7 +23,7 @@ trait MayContainCustomFields
return str_starts_with($attributes, '_snipeit_');
});
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
if (count($request_fields) > 0 && $validator->errors()->isEmpty()) {
if (count($request_fields) > 0) {
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
->each(function ($request_field_name) use ($request_fields, $validator) {
if (CustomField::where('db_column', $request_field_name)->exists()) {
@@ -141,8 +141,6 @@ class ActionlogsTransformer
if ($actionlog->item) {
if ($actionlog->itemType() == 'asset') {
$file_url = route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'accessory') {
$file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'license') {
$file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'user') {
@@ -160,6 +158,7 @@ class ActionlogsTransformer
[
'url' => $file_url,
'filename' => $actionlog->filename,
'inlineable' => (bool) Helper::show_file_inline($actionlog->filename),
] : null,
'item' => ($actionlog->item) ? [
@@ -347,4 +346,4 @@ class ActionlogsTransformer
}
}
@@ -39,7 +39,7 @@ class AssetMaintenancesTransformer
'status_label' => ($assetmaintenance->asset->assetstatus) ? [
'id' => (int) $assetmaintenance->asset->assetstatus->id,
'name'=> e($assetmaintenance->asset->assetstatus->name),
'status_type'=> e($assetmaintenance->asset->assetstatus->status_type),
'status_type'=> e($assetmaintenance->asset->assetstatus->getStatuslabelType()),
'status_meta' => e($assetmaintenance->asset->present()->statusMeta),
] : null,
'company' => (($assetmaintenance->asset) && ($assetmaintenance->asset->company)) ? [
@@ -66,7 +66,7 @@ class AssetMaintenancesTransformer
'completion_date' => Helper::getFormattedDateObject($assetmaintenance->completion_date, 'date'),
'user_id' => ($assetmaintenance->adminuser) ? [
'id' => $assetmaintenance->adminuser->id,
'name'=> e($assetmaintenance->adminuser->present()->fullName())
'name'=> e($assetmaintenance->admin->getFullNameAttribute())
] : null, // legacy to not change the shape of the API
'created_by' => ($assetmaintenance->adminuser) ? [
'id' => (int) $assetmaintenance->adminuser->id,
+3 -2
View File
@@ -8,6 +8,7 @@ use App\Models\Setting;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
class AssetsTransformer
{
@@ -44,8 +45,8 @@ class AssetsTransformer
'status_label' => ($asset->assetstatus) ? [
'id' => (int) $asset->assetstatus->id,
'name'=> e($asset->assetstatus->name),
'status_type'=> e($asset->assetstatus->status_type),
'status_meta' => e($asset->assetstatus->status_type),
'status_type'=> e($asset->assetstatus->getStatuslabelType()),
'status_meta' => e($asset->present()->statusMeta),
] : null,
'category' => (($asset->model) && ($asset->model->category)) ? [
'id' => (int) $asset->model->category->id,
@@ -38,8 +38,6 @@ class ComponentsTransformer
'name' => e($component->category->name),
] : null,
'supplier' => ($component->supplier) ? ['id' => $component->supplier->id, 'name'=> e($component->supplier->name)] : null,
'manufacturer' => ($component->manufacturer) ? ['id' => $component->manufacturer->id, 'name'=> e($component->manufacturer->name)] : null,
'model_number' => ($component->model_number) ? e($component->model_number) : null,
'order_number' => e($component->order_number),
'purchase_date' => Helper::getFormattedDateObject($component->purchase_date, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($component->purchase_cost),
@@ -36,7 +36,6 @@ class ManufacturersTransformer
'licenses_count' => (int) $manufacturer->licenses_count,
'consumables_count' => (int) $manufacturer->consumables_count,
'accessories_count' => (int) $manufacturer->accessories_count,
'components_count' => (int) $manufacturer->components_count,
'created_by' => ($manufacturer->adminuser) ? [
'id' => (int) $manufacturer->adminuser->id,
'name'=> e($manufacturer->adminuser->present()->fullName()),
@@ -24,8 +24,7 @@ class StatuslabelsTransformer
$array = [
'id' => (int) $statuslabel->id,
'name' => e($statuslabel->name),
'type' => $statuslabel->status_type, // legacy - to be removed in later versions
'status_type' => $statuslabel->status_type,
'type' => $statuslabel->getStatuslabelType(),
'color' => ($statuslabel->color) ? e($statuslabel->color) : null,
'show_in_nav' => ($statuslabel->show_in_nav == '1') ? true : false,
'default_label' => ($statuslabel->default_label == '1') ? true : false,
+2 -2
View File
@@ -18,8 +18,8 @@ class AssetImporter extends ItemImporter
$this->defaultStatusLabelId = Statuslabel::first()->id;
if (!is_null(Statuslabel::where('status_type', 'deployable')->first())) {
$this->defaultStatusLabelId = Statuslabel::where('status_type', 'deployable')->first()->id;
if (!is_null(Statuslabel::deployable()->first())) {
$this->defaultStatusLabelId = Statuslabel::deployable()->first()->id;
}
}
+24 -12
View File
@@ -21,6 +21,7 @@ abstract class Importer
* Id of User performing import
* @var
*/
protected $created_by;
/**
* Are we updating items in the import
@@ -128,8 +129,10 @@ abstract class Importer
// However, for testing we also support passing a string directly
if (is_file($file)) {
$this->csv = Reader::createFromPath($file);
$this->csv->setDelimiter($this->delimiter);
} else {
$this->csv = Reader::createFromString($file);
$this->csv->setDelimiter($this->delimiter);
}
$this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
}
@@ -148,28 +151,21 @@ abstract class Importer
{
$headerRow = $this->csv->fetchOne();
$this->csv->setHeaderOffset(0); //explicitly sets the CSV document header record
$results = $this->normalizeInputArray($this->csv->getRecords($headerRow));
$this->populateCustomFields($headerRow);
DB::transaction(function () use ($headerRow) {
$importedItemsCount = 0;
DB::transaction(function () use (&$results) {
Model::unguard();
foreach ($this->csv->getRecords($headerRow) as $row) {
//Lowercase header values to ensure we're comparing values properly.
$row = array_change_key_case($row, CASE_LOWER);
$resultsCount = count($results);
foreach ($results as $row) {
$this->handle($row);
$importedItemsCount++;
if ($this->progressCallback) {
call_user_func($this->progressCallback, $importedItemsCount);
call_user_func($this->progressCallback, $resultsCount);
}
$this->log('------------- Action Summary ----------------');
}
Model::reguard();
});
}
@@ -242,6 +238,22 @@ abstract class Importer
return $key;
}
/**
* Used to lowercase header values to ensure we're comparing values properly.
*
* @param $results
* @return array
*/
public function normalizeInputArray($results)
{
$newArray = [];
foreach ($results as $index => $arrayToNormalize) {
$newArray[$index] = array_change_key_case($arrayToNormalize);
}
return $newArray;
}
/**
* Figure out the fieldname of the custom field
*
@@ -81,12 +81,6 @@ class CustomFieldSetDefaultValuesForModel extends Component
{
$this->fields->each(function ($field) {
$this->selectedValues[$field->db_column] = $this->getSelectedValueForField($field);
// if the element is a checkbox and the value was just sent to null, make it
// an array since Livewire can't bind to non-array values for checkboxes.
if ($field->element === 'checkbox' && is_null($this->selectedValues[$field->db_column])) {
$this->selectedValues[$field->db_column] = [];
}
});
}
+4 -1
View File
@@ -40,6 +40,7 @@ class Importer extends Component
public $consumables_fields;
public $components_fields;
public $aliases_fields;
public $delimiter;
protected $rules = [
'files.*.file_path' => 'required|string',
@@ -47,7 +48,8 @@ class Importer extends Component
'files.*.filesize' => 'required|integer',
'headerRow' => 'array',
'typeOfImport' => 'string',
'field_map' => 'array'
'field_map' => 'array',
'delimiter' => 'in:comma,semicolon,pipe',
];
/**
@@ -196,6 +198,7 @@ class Importer extends Component
'supplier' => trans('general.supplier'),
'purchase_cost' => trans('general.purchase_cost'),
'purchase_date' => trans('general.purchase_date'),
'purchase_order' => trans('admin/licenses/form.purchase_order'),
'asset_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/general.asset')]),
'model_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]),
'manufacturer' => trans('general.manufacturer'),
+26 -17
View File
@@ -43,16 +43,16 @@ class Asset extends Depreciable
/**
* Run after the checkout acceptance was declined by the user
*
*
* @param User $acceptedBy
* @param string $signature
*/
*/
public function declinedCheckout(User $declinedBy, $signature)
{
$this->assigned_to = null;
$this->assigned_type = null;
$this->accepted = null;
$this->save();
$this->accepted = null;
$this->save();
}
/**
@@ -301,9 +301,9 @@ class Asset extends Depreciable
// This asset is not currently assigned to anyone and is not deleted...
if ((! $this->assigned_to) && (! $this->deleted_at)) {
// The asset is not archived and the status is deployable
if (($this->assetstatus) && ($this->archived == '0')
&& ($this->assetstatus->status_type == 'deployable'))
// The asset status is not archived and is deployable
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
&& ($this->assetstatus->deployable == '1'))
{
return true;
@@ -368,7 +368,7 @@ class Asset extends Depreciable
if ($this->save()) {
if (is_int($admin)) {
$checkedOutBy = User::findOrFail($admin);
} elseif ($admin && get_class($admin) === \App\Models\User::class) {
} elseif (get_class($admin) === \App\Models\User::class) {
$checkedOutBy = $admin;
} else {
$checkedOutBy = auth()->user();
@@ -1146,7 +1146,9 @@ class Asset extends Depreciable
public function scopePending($query)
{
return $query->whereHas('assetstatus', function ($query) {
$query->where('status_type', '=', 'deployable');
$query->where('deployable', '=', 0)
->where('pending', '=', 1)
->where('archived', '=', 0);
});
}
@@ -1193,7 +1195,9 @@ class Asset extends Depreciable
{
return $query->whereNull('assets.assigned_to')
->whereHas('assetstatus', function ($query) {
$query->where('status_type', '=', 'deployable');
$query->where('deployable', '=', 1)
->where('pending', '=', 0)
->where('archived', '=', 0);
});
}
@@ -1208,7 +1212,9 @@ class Asset extends Depreciable
public function scopeUndeployable($query)
{
return $query->whereHas('assetstatus', function ($query) {
$query->where('status_type', '!=', 'deployable');
$query->where('deployable', '=', 0)
->where('pending', '=', 0)
->where('archived', '=', 0);
});
}
@@ -1223,7 +1229,7 @@ class Asset extends Depreciable
public function scopeNotArchived($query)
{
return $query->whereHas('assetstatus', function ($query) {
$query->where('status_type', '!=', 'archived');
$query->where('archived', '=', 0);
});
}
@@ -1400,7 +1406,9 @@ class Asset extends Depreciable
public function scopeArchived($query)
{
return $query->whereHas('assetstatus', function ($query) {
$query->where('status_type', '=', 'archived');
$query->where('deployable', '=', 0)
->where('pending', '=', 0)
->where('archived', '=', 1);
});
}
@@ -1432,8 +1440,9 @@ class Asset extends Depreciable
return Company::scopeCompanyables($query->where($table.'.requestable', '=', 1))
->whereHas('assetstatus', function ($query) {
$query->where(function ($query) {
$query->where('status_type', '!=', 'archived'); // you definitely can't request something that's archived
}); // we've decided that even though an asset may be 'pending', you can still request it
$query->where('deployable', '=', 1)
->where('archived', '=', 0); // you definitely can't request something that's archived
})->orWhere('pending', '=', 1); // we've decided that even though an asset may be 'pending', you can still request it
});
}
@@ -1696,7 +1705,7 @@ class Asset extends Depreciable
});
});
}
/**
* THIS CLUNKY BIT IS VERY IMPORTANT
@@ -1717,7 +1726,7 @@ class Asset extends Depreciable
* assets.location would fail, as that field doesn't exist -- plus we're already searching
* against those relationships earlier in this method.
*
* - snipe
* - snipe
*
*/
+1 -1
View File
@@ -176,7 +176,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by')
return $this->belongsTo(\App\Models\User::class, 'user_id')
->withTrashed();
}
+1 -1
View File
@@ -116,7 +116,7 @@ final class Company extends SnipeModel
if ($current_user->company_id != null) {
return $current_user->company_id;
} else {
return null;
return static::getIdFromInput($unescaped_input);
}
}
}
+3
View File
@@ -8,6 +8,9 @@ trait CompanyableTrait
* This trait is used to scope models to the current company. To use this scope on companyable models,
* we use the "use Companyable;" statement at the top of the mode.
*
* We CANNOT USE THIS ON USERS, as it causes an infinite loop and prevents users from logging in, since this scope will be
* applied to the currently logged in (or logging in) user in addition to the user model for viewing lists of users.
*
* @see \App\Models\Company\Company::scopeCompanyables()
* @return void
*/
+1 -39
View File
@@ -38,7 +38,6 @@ class Component extends SnipeModel
'min_amt' => 'integer|min:0|nullable',
'purchase_date' => 'date_format:Y-m-d|nullable',
'purchase_cost' => 'numeric|nullable|gte:0|max:9999999999999',
'manufacturer_id' => 'integer|exists:manufacturers,id|nullable',
];
/**
@@ -61,8 +60,6 @@ class Component extends SnipeModel
'company_id',
'supplier_id',
'location_id',
'manufacturer_id',
'model_number',
'name',
'purchase_cost',
'purchase_date',
@@ -80,15 +77,7 @@ class Component extends SnipeModel
*
* @var array
*/
protected $searchableAttributes = [
'name',
'order_number',
'serial',
'purchase_cost',
'purchase_date',
'notes',
'model_number',
];
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date', 'notes'];
/**
* The relations and their attributes that should be included when searching the model.
@@ -100,7 +89,6 @@ class Component extends SnipeModel
'company' => ['name'],
'location' => ['name'],
'supplier' => ['name'],
'manufacturer' => ['name'],
];
@@ -195,19 +183,6 @@ class Component extends SnipeModel
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id');
}
/**
* Establishes the item -> manufacturer relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
{
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
}
/**
* Establishes the component -> action logs relationship
*
@@ -336,19 +311,6 @@ class Component extends SnipeModel
return $query->leftJoin('suppliers', 'components.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);
}
/**
* Query builder scope to order on manufacturer
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderManufacturer($query, $order)
{
return $query->leftJoin('manufacturers', 'components.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
}
public function scopeOrderByCreatedBy($query, $order)
{
return $query->leftJoin('users as admin_sort', 'components.created_by', '=', 'admin_sort.id')->select('components.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
+33 -3
View File
@@ -2,16 +2,46 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class Import extends Model
{
use HasFactory;
protected $casts = [
'header_row' => 'array',
'first_row' => 'array',
'field_map' => 'json',
];
protected function delimiter(): Attribute
{
return Attribute::make(
set: function ($value) {
if ($value == 'semicolon') {
return ';';
}
if ($value == 'pipe') {
return '|';
}
return ',';
},
get: function ($value) {
if ($value == 'semicolon') {
return ';';
}
if ($value == 'pipe') {
return '|';
}
return ',';
}
);
}
}
+4 -2
View File
@@ -42,7 +42,7 @@ class Location extends SnipeModel
];
/**
* Whether the model should inject its identifier to the unique
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
@@ -138,7 +138,9 @@ class Location extends SnipeModel
{
return $this->hasMany(\App\Models\Asset::class, 'location_id')
->whereHas('assetstatus', function ($query) {
$query->whereNot('status_labels.status_type', '=', 'archived');
$query->where('status_labels.deployable', '=', 1)
->orWhere('status_labels.pending', '=', 1)
->orWhere('status_labels.archived', '=', 0);
});
}
+34
View File
@@ -117,6 +117,7 @@ trait Loggable
*/
public function logCheckin($target, $note, $action_date = null, $originalValues = [])
{
$settings = Setting::getSettings();
$log = new Actionlog;
if($target != null){
@@ -170,6 +171,39 @@ trait Loggable
$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;
}
-5
View File
@@ -78,7 +78,6 @@ class Manufacturer extends SnipeModel
&& (($this->licenses_count ?? $this->licenses()->count()) === 0)
&& (($this->consumables_count ?? $this->consumables()->count()) === 0)
&& (($this->accessories_count ?? $this->accessories()->count()) === 0)
&& (($this->components_count ?? $this->components()->count()) === 0)
&& ($this->deleted_at == '');
}
@@ -107,10 +106,6 @@ class Manufacturer extends SnipeModel
return $this->hasMany(\App\Models\Consumable::class, 'manufacturer_id');
}
public function components()
{
return $this->hasMany(\App\Models\Component::class, 'manufacturer_id');
}
public function adminuser()
{
+29
View File
@@ -51,7 +51,36 @@ class Setting extends Model
*/
protected $rules = [
'brand' => 'required|min:1|numeric',
'qr_text' => 'max:31|nullable',
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email|nullable',
'default_currency' => 'required',
'locale' => 'required',
'labels_per_page' => 'numeric',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_pmargin_left' => 'numeric|nullable',
'labels_pmargin_right' => 'numeric|nullable',
'labels_pmargin_top' => 'numeric|nullable',
'labels_pmargin_bottom' => 'numeric|nullable',
'labels_display_bgutter' => 'numeric|nullable',
'labels_display_sgutter' => 'numeric|nullable',
'labels_fontsize' => 'numeric|min:5',
'labels_pagewidth' => 'numeric|nullable',
'labels_pageheight' => 'numeric|nullable',
'login_remote_user_enabled' => 'numeric|nullable',
'login_common_disabled' => 'numeric|nullable',
'login_remote_user_custom_logout_url' => 'string|nullable',
'login_remote_user_header_name' => 'string|nullable',
'thumbnail_max_h' => 'numeric|max:500|min:25',
'pwd_secure_min' => 'numeric|required|min:8',
'alert_threshold' => 'numeric|nullable',
'alert_interval' => 'numeric|nullable',
'audit_warning_days' => 'numeric|nullable',
'due_checkin_days' => 'numeric|nullable',
'audit_interval' => 'numeric|nullable',
'custom_forgot_pass_url' => 'url|nullable',
'privacy_policy_link' => 'nullable|url',
'google_client_id' => 'nullable|ends_with:apps.googleusercontent.com'
];
+85 -6
View File
@@ -23,16 +23,17 @@ class Statuslabel extends SnipeModel
protected $rules = [
'name' => 'required|string|unique_undeleted',
'notes' => 'string|nullable',
'status_type' => 'required|in:deployable,pending,archived,undeployable1',
'deployable' => 'required',
'pending' => 'required',
'archived' => 'required',
];
protected $fillable = [
'status_type',
'archived',
'deployable',
'name',
'notes',
'color',
'show_in_nav',
'default_label',
'pending',
];
use Searchable;
@@ -75,6 +76,54 @@ class Statuslabel extends SnipeModel
* @since [v1.0]
* @return string
*/
public function getStatuslabelType()
{
if (($this->pending == '1') && ($this->archived == '0') && ($this->deployable == '0')) {
return 'pending';
} elseif (($this->pending == '0') && ($this->archived == '1') && ($this->deployable == '0')) {
return 'archived';
} elseif (($this->pending == '0') && ($this->archived == '0') && ($this->deployable == '0')) {
return 'undeployable';
}
return 'deployable';
}
/**
* Query builder scope to for pending status types
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopePending()
{
return $this->where('pending', '=', 1)
->where('archived', '=', 0)
->where('deployable', '=', 0);
}
/**
* Query builder scope for archived status types
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeArchived()
{
return $this->where('pending', '=', 0)
->where('archived', '=', 1)
->where('deployable', '=', 0);
}
/**
* Query builder scope for deployable status types
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeDeployable()
{
return $this->where('pending', '=', 0)
->where('archived', '=', 0)
->where('deployable', '=', 1);
}
/**
* Query builder scope for undeployable status types
@@ -83,10 +132,40 @@ class Statuslabel extends SnipeModel
*/
public function scopeUndeployable()
{
return $this->whereNot('status_type', '=', 'deployable');
return $this->where('pending', '=', 0)
->where('archived', '=', 0)
->where('deployable', '=', 0);
}
/**
* Helper function to determine type attributes
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return string
*/
public static function getStatuslabelTypesForDB($type)
{
$statustype['pending'] = 0;
$statustype['deployable'] = 0;
$statustype['archived'] = 0;
if ($type == 'pending') {
$statustype['pending'] = 1;
$statustype['deployable'] = 0;
$statustype['archived'] = 0;
} elseif ($type == 'deployable') {
$statustype['pending'] = 0;
$statustype['deployable'] = 1;
$statustype['archived'] = 0;
} elseif ($type == 'archived') {
$statustype['pending'] = 0;
$statustype['deployable'] = 0;
$statustype['archived'] = 1;
}
return $statustype;
}
public function scopeOrderByCreatedBy($query, $order)
{
@@ -38,7 +38,6 @@ class CheckoutConsumableNotification extends Notification
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $consumable->checkout_qty;
$this->settings = Setting::getSettings();
}
@@ -174,6 +173,7 @@ class CheckoutConsumableNotification extends Notification
*/
public function toMail()
{
Log::debug($this->item->getImageUrl());
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
@@ -188,7 +188,6 @@ class CheckoutConsumableNotification extends Notification
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'qty' => $this->qty,
])
->subject(trans('mail.Confirm_consumable_delivery'));
}
@@ -116,6 +116,12 @@ class AssetMaintenancesPresenter extends Presenter
'sortable' => true,
'title' => trans('admin/asset_maintenances/form.cost'),
'class' => 'text-right',
], [
'field' => 'user_id',
'searchable' => true,
'sortable' => true,
'title' => trans('general.admin'),
'formatter' => 'usersLinkObjFormatter',
], [
'field' => 'created_by',
'searchable' => false,
+13
View File
@@ -479,6 +479,19 @@ class AssetPresenter extends Presenter
return $interval;
}
/**
* @return string
* This handles the status label "meta" status of "deployed" if
* it's assigned. Should maybe deprecate.
*/
public function statusMeta()
{
if ($this->model->assigned) {
return 'deployed';
}
return $this->model->assetstatus->getStatuslabelType();
}
/**
* @return string
+2 -14
View File
@@ -66,20 +66,8 @@ class ComponentPresenter extends Presenter
'title' => trans('general.supplier'),
'visible' => false,
'formatter' => 'suppliersLinkObjFormatter',
], [
'field' => 'model_number',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/models/table.modelnumber'),
], [
'field' => 'manufacturer',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.manufacturer'),
'visible' => false,
'formatter' => 'manufacturersLinkObjFormatter',
], [
],
[
'field' => 'qty',
'searchable' => false,
'sortable' => true,
@@ -311,7 +311,7 @@ class DepreciationReportPresenter extends Presenter
if ($this->model->assigned) {
return 'deployed';
}
return $this->model->assetstatus->status_type;
return $this->model->assetstatus->getStatuslabelType();
}
/**
+2 -9
View File
@@ -124,15 +124,8 @@ class ManufacturerPresenter extends Presenter
'title' => trans('general.accessories'),
'visible' => true,
'class' => 'css-accessory',
], [
'field' => 'components_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.components'),
'visible' => true,
'class' => 'css-component',
], [
],
[
'field' => 'created_by',
'searchable' => false,
'sortable' => true,
+3 -3
View File
@@ -30,9 +30,9 @@ class StatusLabelPresenter extends Presenter
'visible' => true,
'formatter' => 'statuslabelsAssetLinkFormatter',
],[
'field' => 'status_type',
'searchable' => true,
'sortable' => true,
'field' => 'type',
'searchable' => false,
'sortable' => false,
'switchable' => false,
'title' => trans('admin/statuslabels/table.status_type'),
'visible' => true,
+1 -2
View File
@@ -31,7 +31,6 @@ class ValidationServiceProvider extends ServiceProvider
Validator::extend('email_array', function ($attribute, $value, $parameters, $validator) {
$value = str_replace(' ', '', $value);
$array = explode(',', $value);
$email_to_validate = [];
foreach ($array as $email) { //loop over values
$email_to_validate['alert_email'][] = $email;
@@ -39,7 +38,7 @@ class ValidationServiceProvider extends ServiceProvider
$rules = ['alert_email.*'=>'email'];
$messages = [
'alert_email.*' => trans('validation.custom.email_array'),
'alert_email.*'=>trans('validation.email_array'),
];
$validator = Validator::make($email_to_validate, $rules, $messages);
@@ -40,7 +40,7 @@ class AssetCannotBeCheckedOutToNondeployableStatus implements DataAwareRule, Val
// Check to see if any of the assign-ish fields are set
if ((isset($this->data['assigned_to'])) || (isset($this->data['assigned_user'])) || (isset($this->data['assigned_location'])) || (isset($this->data['assigned_asset'])) || (isset($this->data['assigned_type']))) {
if (($value) && ($label = Statuslabel::find($value)) && ($label->status_type!='deployable')) {
if (($value) && ($label = Statuslabel::find($value)) && ($label->getStatuslabelType()!='deployable')) {
$fail(trans('admin/hardware/form.asset_not_deployable'));
}
+1
View File
@@ -74,6 +74,7 @@
"ext-exif": "*"
},
"require-dev": {
"brianium/paratest": "^7.0",
"fakerphp/faker": "^1.16",
"larastan/larastan": "^2.9",
"mockery/mockery": "^1.4",
Generated
+841 -622
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -280,6 +280,7 @@ return [
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
// Illuminate\Translation\TranslationServiceProvider::class, //replaced on next line
App\Providers\SnipeTranslationServiceProvider::class, //we REPLACE the default Laravel translator with our own
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
@@ -372,7 +373,7 @@ return [
'Image' => Intervention\Image\ImageServiceProvider::class,
'Carbon' => Carbon\Carbon::class,
'Helper' => App\Helpers\Helper::class,
'StorageHelper' => App\Helpers\StorageHelper::class,
// makes it much easier to use 'Helper::blah' in blades (which is where we usually use this)
'Icon' => App\Helpers\IconHelper::class,
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
+4 -4
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v7.0.13',
'full_app_version' => 'v7.0.13 - build 15666-g03b01689b',
'build_version' => '15666',
'full_app_version' => 'v7.0.13 - build 15514-gdc0949da7',
'build_version' => '15514',
'prerelease_version' => '',
'hash_version' => 'g03b01689b',
'full_hash' => 'v7.0.13-144-g03b01689b',
'hash_version' => 'gdc0949da7',
'full_hash' => 'v7.0.13-265-gdc0949da7',
'branch' => 'develop',
);
+1 -15
View File
@@ -3,6 +3,7 @@
namespace Database\Factories;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Category;
use App\Models\Location;
use App\Models\Manufacturer;
@@ -155,19 +156,4 @@ class AccessoryFactory extends Factory
]);
});
}
public function checkedOutToUsers(array $users)
{
return $this->afterCreating(function (Accessory $accessory) use ($users) {
foreach ($users as $user) {
$accessory->checkouts()->create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => 1,
'assigned_to' => $user->id,
'assigned_type' => User::class,
]);
}
});
}
}
+1 -2
View File
@@ -34,8 +34,7 @@ class AssetFactory extends Factory
'rtd_location_id' => Location::factory(),
'serial' => $this->faker->uuid(),
'status_id' => function () {
// $status = Statuslabel::factory()->create(); dd($status) ;
return Statuslabel::where('status_type', 'deployable')->first() ?? Statuslabel::factory()->create(['name' => 'Ready to Deploy', 'status_type' => 'deployable'])->id;
return Statuslabel::where('name', 'Ready to Deploy')->first() ?? Statuslabel::factory()->rtd()->create(['name' => 'Ready to Deploy']);
},
'created_by' => User::factory()->superuser(),
'asset_tag' => $this->faker->unixTime('now'),
+4 -15
View File
@@ -7,7 +7,6 @@ use App\Models\Asset;
use App\Models\Category;
use App\Models\Company;
use App\Models\Component;
use App\Models\Manufacturer;
use App\Models\Consumable;
use App\Models\Location;
use App\Models\User;
@@ -31,7 +30,6 @@ class ComponentFactory extends Factory
*/
public function definition()
{
return [
'name' => $this->faker->text(20),
'category_id' => Category::factory(),
@@ -44,14 +42,12 @@ class ComponentFactory extends Factory
'min_amt' => $this->faker->numberBetween($min = 1, $max = 2),
'company_id' => Company::factory(),
'supplier_id' => Supplier::factory(),
'model_number' => $this->faker->numberBetween(1000000, 50000000),
];
}
public function ramCrucial4()
{
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
return $this->state(function () use ($manufacturer) {
return $this->state(function () {
return [
'name' => 'Crucial 4GB DDR3L-1600 SODIMM',
'category_id' => function () {
@@ -59,7 +55,6 @@ class ComponentFactory extends Factory
},
'qty' => 10,
'min_amt' => 2,
'manufacturer_id' => $manufacturer->id,
'location_id' => Location::factory(),
];
});
@@ -67,8 +62,7 @@ class ComponentFactory extends Factory
public function ramCrucial8()
{
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
return $this->state(function () use ($manufacturer) {
return $this->state(function () {
return [
'name' => 'Crucial 8GB DDR3L-1600 SODIMM Memory for Mac',
'category_id' => function () {
@@ -76,15 +70,13 @@ class ComponentFactory extends Factory
},
'qty' => 10,
'min_amt' => 2,
'manufacturer_id' => $manufacturer->id,
];
});
}
public function ssdCrucial120()
{
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
return $this->state(function () use ($manufacturer) {
return $this->state(function () {
return [
'name' => 'Crucial BX300 120GB SATA Internal SSD',
'category_id' => function () {
@@ -92,15 +84,13 @@ class ComponentFactory extends Factory
},
'qty' => 10,
'min_amt' => 2,
'manufacturer_id' => $manufacturer->id,
];
});
}
public function ssdCrucial240()
{
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
return $this->state(function () use ($manufacturer) {
return $this->state(function () {
return [
'name' => 'Crucial BX300 240GB SATA Internal SSD',
'category_id' => function () {
@@ -108,7 +98,6 @@ class ComponentFactory extends Factory
},
'qty' => 10,
'min_amt' => 2,
'manufacturer_id' => $manufacturer->id,
];
});
}
-146
View File
@@ -1,146 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Import;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
use Tests\Support\Importing;
/**
* @extends Factory<Import>
*/
class ImportFactory extends Factory
{
/**
* @inheritdoc
*/
protected $model = Import::class;
/**
* @inheritdoc
*/
public function definition()
{
return [
'name' => $this->faker->company,
'file_path' => Str::random().'.csv',
'filesize' => $this->faker->randomDigitNotNull(),
'field_map' => null,
];
}
/**
* Create an accessory import type.
*
* @return static
*/
public function accessory()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\AccessoriesImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Accessories";
$attributes['import_type'] = 'accessory';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
/**
* Create an asset import type.
*
* @return static
*/
public function asset()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\AssetsImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Assets";
$attributes['import_type'] = 'asset';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
/**
* Create a component import type.
*
* @return static
*/
public function component()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\ComponentsImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Components";
$attributes['import_type'] = 'component';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
/**
* Create a consumable import type.
*
* @return static
*/
public function consumable()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\ConsumablesImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Consumables";
$attributes['import_type'] = 'consumable';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
/**
* Create a license import type.
*
* @return static
*/
public function license()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\LicensesImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Licenses";
$attributes['import_type'] = 'license';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
/**
* Create a users import type.
*
* @return static
*/
public function users()
{
return $this->state(function (array $attributes) {
$fileBuilder = Importing\UsersImportFileBuilder::new();
$attributes['name'] = "{$attributes['name']} Employees";
$attributes['import_type'] = 'user';
$attributes['header_row'] = $fileBuilder->toCsv()[0];
$attributes['first_row'] = $fileBuilder->firstRow();
return $attributes;
});
}
}
+6 -8
View File
@@ -28,7 +28,9 @@ class StatuslabelFactory extends Factory
'updated_at' => $this->faker->dateTime(),
'created_by' => User::factory()->superuser(),
'deleted_at' => null,
'status_type' => 'deployable',
'deployable' => 0,
'pending' => 0,
'archived' => 0,
'notes' => '',
];
}
@@ -38,7 +40,7 @@ class StatuslabelFactory extends Factory
return $this->state(function () {
return [
'notes' => $this->faker->sentence(),
'status_type' => 'deployable',
'deployable' => 1,
'default_label' => 1,
];
});
@@ -54,7 +56,7 @@ class StatuslabelFactory extends Factory
return $this->state(function () {
return [
'notes' => $this->faker->sentence(),
'status_type' => 'pending',
'pending' => 1,
'default_label' => 1,
];
});
@@ -65,8 +67,8 @@ class StatuslabelFactory extends Factory
return $this->state(function () {
return [
'notes' => 'These assets are permanently undeployable',
'archived' => 1,
'default_label' => 0,
'status_type' => 'archived',
];
});
}
@@ -77,7 +79,6 @@ class StatuslabelFactory extends Factory
return [
'name' => 'Out for Diagnostics',
'default_label' => 0,
'status_type' => 'pending',
];
});
}
@@ -88,7 +89,6 @@ class StatuslabelFactory extends Factory
return [
'name' => 'Out for Repair',
'default_label' => 0,
'status_type' => 'pending',
];
});
}
@@ -99,7 +99,6 @@ class StatuslabelFactory extends Factory
return [
'name' => 'Broken - Not Fixable',
'default_label' => 0,
'status_type' => 'archived',
];
});
}
@@ -110,7 +109,6 @@ class StatuslabelFactory extends Factory
return [
'name' => 'Lost/Stolen',
'default_label' => 0,
'status_type' => 'archived',
];
});
}
-3
View File
@@ -7,9 +7,6 @@ use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use \Auth;
/**
* @extends Factory<User>
*/
class UserFactory extends Factory
{
/**
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('imports', function (Blueprint $table) {
$table->string('delimiter', 1)->nullable()->default(',');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('imports', function (Blueprint $table) {
$table->dropColumn('delimiter');
});
}
};
@@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('components', function (Blueprint $table) {
$table->integer('manufacturer_id')->after('purchase_cost')->nullable()->default(null);
$table->string('model_number')->after('purchase_cost')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('components', function (Blueprint $table) {
$table->dropColumn('manufacturer_id');
$table->dropColumn('model_number');
});
}
};
@@ -1,71 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use App\Models\StatusLabel;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (!Schema::hasColumn('status_labels', 'status_type')) {
Schema::table('status_labels', function (Blueprint $table) {
$table->string('status_type')->after('name')->default('deployable');
});
DB::table('status_labels')->where('pending', 1)->update(['status_type' => 'pending']);
DB::table('status_labels')->where('archived', 1)->update(['status_type' => 'archived']);
DB::table('status_labels')->where('deployable', 1)->update(['status_type' => 'deployable']);
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('deployable', 'legacy_deployable');
});
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('pending', 'legacy_pending');
});
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('archived', 'legacy_archived');
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
if (Schema::hasColumn('status_labels', 'status_type')) {
Schema::table('status_labels', function (Blueprint $table) {
$table->dropColumn('status_type');
});
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('legacy_deployable', 'deployable');
});
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('legacy_pending', 'pending');
});
Schema::table('status_labels', function (Blueprint $table) {
$table->renameColumn('legacy_archived', 'archived');
});
}
}
};
-1
View File
@@ -37,7 +37,6 @@ class SettingsSeeder extends Seeder
$settings->support_footer = 'on';
$settings->pwd_secure_min = '8';
$settings->default_avatar = 'default.png';
$settings->show_archived_in_list = 0;
$settings->save();
if ($user = User::where('username', '=', 'admin')->first()) {
+4 -10
View File
@@ -17,27 +17,21 @@ class StatuslabelSeeder extends Seeder
Statuslabel::factory()->rtd()->create([
'name' => 'Ready to Deploy',
'created_by' => $admin->id,
'status_type' => 'deployable',
'legacy_deployable' => 1,
]);
Statuslabel::factory()->pending()->create([
'name' => 'Pending',
'created_by' => $admin->id,
'status_type' => 'pending',
'legacy_pending' => 1,
]);
Statuslabel::factory()->archived()->create([
'name' => 'Archived',
'created_by' => $admin->id,
'status_type' => 'archived',
'legacy_archived' => 1,
]);
Statuslabel::factory()->outForDiagnostics()->pending()->create(['created_by' => $admin->id]);
Statuslabel::factory()->outForRepair()->pending()->create(['created_by' => $admin->id]);
Statuslabel::factory()->broken()->archived()->create(['created_by' => $admin->id]);
Statuslabel::factory()->lost()->archived()->create(['created_by' => $admin->id]);
Statuslabel::factory()->outForDiagnostics()->create(['created_by' => $admin->id]);
Statuslabel::factory()->outForRepair()->create(['created_by' => $admin->id]);
Statuslabel::factory()->broken()->create(['created_by' => $admin->id]);
Statuslabel::factory()->lost()->create(['created_by' => $admin->id]);
}
}
+4 -10
View File
@@ -942,21 +942,20 @@ h4 {
background-color: #f9f9f9;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-striped .row:nth-of-type(even) div {
background: #FFFFFF;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-new-striped {
vertical-align: top;
padding: 3px;
line-height: 2.6;
padding: 0px;
margin-left: 20px;
display: table;
width: 100%;
word-wrap: break-word;
table-layout: fixed;
padding-right: 20px;
}
/**
* NEW STRIPING
@@ -966,25 +965,20 @@ h4 {
.row-new-striped > .row:nth-of-type(even) {
background: #FFFFFF;
border-top: 1px solid #dddddd;
line-height: 1.9;
display: table-row;
}
.row-new-striped > .row:nth-of-type(odd) {
background-color: #F8F8F8;
border-top: 1px solid #dddddd;
display: table-row;
line-height: 1.9;
padding: 2px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div[class^="col"]:first-child {
font-weight: bold;
+4 -10
View File
@@ -574,21 +574,20 @@ h4 {
background-color: #f9f9f9;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-striped .row:nth-of-type(even) div {
background: #FFFFFF;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-new-striped {
vertical-align: top;
padding: 3px;
line-height: 2.6;
padding: 0px;
margin-left: 20px;
display: table;
width: 100%;
word-wrap: break-word;
table-layout: fixed;
padding-right: 20px;
}
/**
* NEW STRIPING
@@ -598,25 +597,20 @@ h4 {
.row-new-striped > .row:nth-of-type(even) {
background: #FFFFFF;
border-top: 1px solid #dddddd;
line-height: 1.9;
display: table-row;
}
.row-new-striped > .row:nth-of-type(odd) {
background-color: #F8F8F8;
border-top: 1px solid #dddddd;
display: table-row;
line-height: 1.9;
padding: 2px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div[class^="col"]:first-child {
font-weight: bold;
+8 -20
View File
@@ -21914,21 +21914,20 @@ h4 {
background-color: #f9f9f9;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-striped .row:nth-of-type(even) div {
background: #FFFFFF;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-new-striped {
vertical-align: top;
padding: 3px;
line-height: 2.6;
padding: 0px;
margin-left: 20px;
display: table;
width: 100%;
word-wrap: break-word;
table-layout: fixed;
padding-right: 20px;
}
/**
* NEW STRIPING
@@ -21938,25 +21937,20 @@ h4 {
.row-new-striped > .row:nth-of-type(even) {
background: #FFFFFF;
border-top: 1px solid #dddddd;
line-height: 1.9;
display: table-row;
}
.row-new-striped > .row:nth-of-type(odd) {
background-color: #F8F8F8;
border-top: 1px solid #dddddd;
display: table-row;
line-height: 1.9;
padding: 2px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div[class^="col"]:first-child {
font-weight: bold;
@@ -23395,21 +23389,20 @@ h4 {
background-color: #f9f9f9;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-striped .row:nth-of-type(even) div {
background: #FFFFFF;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-new-striped {
vertical-align: top;
padding: 3px;
line-height: 2.6;
padding: 0px;
margin-left: 20px;
display: table;
width: 100%;
word-wrap: break-word;
table-layout: fixed;
padding-right: 20px;
}
/**
* NEW STRIPING
@@ -23419,25 +23412,20 @@ h4 {
.row-new-striped > .row:nth-of-type(even) {
background: #FFFFFF;
border-top: 1px solid #dddddd;
line-height: 1.9;
display: table-row;
}
.row-new-striped > .row:nth-of-type(odd) {
background-color: #F8F8F8;
border-top: 1px solid #dddddd;
display: table-row;
line-height: 1.9;
padding: 2px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div[class^="col"]:first-child {
font-weight: bold;
+3 -3
View File
@@ -2,8 +2,8 @@
"/js/build/app.js": "/js/build/app.js?id=5e9ac5c1a7e089f056fb1dba566193a6",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4",
"/css/build/overrides.css": "/css/build/overrides.css?id=1c3ffc5fb379e21523f2a9b03f986edb",
"/css/build/app.css": "/css/build/app.css?id=d04f32982fb319ac35a32d362089f18b",
"/css/build/overrides.css": "/css/build/overrides.css?id=efd9f439cb0586512d03172bcd9a5752",
"/css/build/app.css": "/css/build/app.css?id=2f45befb40b9d7f038eeae9569c33a5f",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7",
@@ -19,7 +19,7 @@
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/all.css": "/css/dist/all.css?id=9f69886d7a8e4c383cd09a48573922b7",
"/css/dist/all.css": "/css/dist/all.css?id=e9509d7591637153f667461642e47e30",
"/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",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
+93 -227
View File
@@ -1432,10 +1432,10 @@ var require_module_cjs = __commonJS({
});
}
function cleanupElement(el) {
var _a, _b;
(_a = el._x_effects) == null ? void 0 : _a.forEach(dequeueJob);
while ((_b = el._x_cleanups) == null ? void 0 : _b.length)
el._x_cleanups.pop()();
if (el._x_cleanups) {
while (el._x_cleanups.length)
el._x_cleanups.pop()();
}
}
var observer = new MutationObserver(onMutate);
var currentlyObserving = false;
@@ -1673,23 +1673,27 @@ var require_module_cjs = __commonJS({
magics[name] = callback;
}
function injectMagics(obj, el) {
let memoizedUtilities = getUtilities(el);
Object.entries(magics).forEach(([name, callback]) => {
let memoizedUtilities = null;
function getUtilities() {
if (memoizedUtilities) {
return memoizedUtilities;
} else {
let [utilities, cleanup] = getElementBoundUtilities(el);
memoizedUtilities = { interceptor, ...utilities };
onElRemoved(el, cleanup);
return memoizedUtilities;
}
}
Object.defineProperty(obj, `$${name}`, {
get() {
return callback(el, memoizedUtilities);
return callback(el, getUtilities());
},
enumerable: false
});
});
return obj;
}
function getUtilities(el) {
let [utilities, cleanup] = getElementBoundUtilities(el);
let utils = { interceptor, ...utilities };
onElRemoved(el, cleanup);
return utils;
}
function tryCatch(el, expression, callback, ...args) {
try {
return callback(...args);
@@ -2063,8 +2067,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
function destroyTree(root, walker = walk) {
walker(root, (el) => {
cleanupElement(el);
cleanupAttributes(el);
cleanupElement(el);
});
}
function warnAboutMissingPlugins() {
@@ -2557,7 +2561,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
}
function bindInputValue(el, value) {
if (isRadio(el)) {
if (el.type === "radio") {
if (el.attributes.value === void 0) {
el.value = value;
}
@@ -2568,7 +2572,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
el.checked = checkedAttrLooseCompare(el.value, value);
}
}
} else if (isCheckbox(el)) {
} else if (el.type === "checkbox") {
if (Number.isInteger(value)) {
el.value = value;
} else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) {
@@ -2644,37 +2648,34 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
return rawValue ? Boolean(rawValue) : null;
}
var booleanAttributes = /* @__PURE__ */ new Set([
"allowfullscreen",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"inert",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected",
"shadowrootclonable",
"shadowrootdelegatesfocus",
"shadowrootserializable"
]);
function isBooleanAttr(attrName) {
return booleanAttributes.has(attrName);
const booleanAttributes = [
"disabled",
"checked",
"required",
"readonly",
"open",
"selected",
"autofocus",
"itemscope",
"multiple",
"novalidate",
"allowfullscreen",
"allowpaymentrequest",
"formnovalidate",
"autoplay",
"controls",
"loop",
"muted",
"playsinline",
"default",
"ismap",
"reversed",
"async",
"defer",
"nomodule"
];
return booleanAttributes.includes(attrName);
}
function attributeShouldntBePreservedIfFalsy(name) {
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
@@ -2707,12 +2708,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
return attr;
}
function isCheckbox(el) {
return el.type === "checkbox" || el.localName === "ui-checkbox" || el.localName === "ui-switch";
}
function isRadio(el) {
return el.type === "radio" || el.localName === "ui-radio";
}
function debounce2(func, wait) {
var timeout;
return function() {
@@ -2781,10 +2776,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
return stores[name];
}
stores[name] = value;
initInterceptors(stores[name]);
if (typeof value === "object" && value !== null && value.hasOwnProperty("init") && typeof value.init === "function") {
stores[name].init();
}
initInterceptors(stores[name]);
}
function getStores() {
return stores;
@@ -2866,7 +2861,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
get raw() {
return raw;
},
version: "3.14.3",
version: "3.14.1",
flushAndStopDeferringMutations,
dontAutoEvaluateFunctions,
disableEffectScheduling,
@@ -3075,10 +3070,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
placeInDom(el._x_teleport, target2, modifiers);
});
};
cleanup(() => mutateDom(() => {
clone2.remove();
destroyTree(clone2);
}));
cleanup(() => clone2.remove());
});
var teleportContainerDuringClone = document.createElement("div");
function getTarget(expression) {
@@ -3302,7 +3294,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
setValue(getInputValue(el, modifiers, e, getValue()));
});
if (modifiers.includes("fill")) {
if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
setValue(getInputValue(el, modifiers, { target: el }, getValue()));
}
}
@@ -3342,7 +3334,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
return mutateDom(() => {
if (event instanceof CustomEvent && event.detail !== void 0)
return event.detail !== null && event.detail !== void 0 ? event.detail : event.target.value;
else if (isCheckbox(el)) {
else if (el.type === "checkbox") {
if (Array.isArray(currentValue)) {
let newValue = null;
if (modifiers.includes("number")) {
@@ -3373,7 +3365,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
} else {
let newValue;
if (isRadio(el)) {
if (el.type === "radio") {
if (event.target.checked) {
newValue = event.target.value;
} else {
@@ -3566,10 +3558,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
el._x_lookup = {};
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
cleanup(() => {
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
destroyTree(el2);
el2.remove();
}));
Object.values(el._x_lookup).forEach((el2) => el2.remove());
delete el._x_prevKeys;
delete el._x_lookup;
});
@@ -3638,12 +3627,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
for (let i = 0; i < removes.length; i++) {
let key = removes[i];
if (!(key in lookup))
continue;
mutateDom(() => {
destroyTree(lookup[key]);
lookup[key].remove();
});
if (!!lookup[key]._x_effects) {
lookup[key]._x_effects.forEach(dequeueJob);
}
lookup[key].remove();
lookup[key] = null;
delete lookup[key];
}
for (let i = 0; i < moves.length; i++) {
@@ -3764,10 +3752,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
el._x_currentIfEl = clone2;
el._x_undoIf = () => {
mutateDom(() => {
destroyTree(clone2);
clone2.remove();
walk(clone2, (node) => {
if (!!node._x_effects) {
node._x_effects.forEach(dequeueJob);
}
});
clone2.remove();
delete el._x_currentIfEl;
};
return clone2;
@@ -3822,9 +3812,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
});
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js
// ../alpine/packages/collapse/dist/module.cjs.js
var require_module_cjs2 = __commonJS({
"../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js"(exports, module) {
"../alpine/packages/collapse/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
@@ -3897,7 +3887,7 @@ var require_module_cjs2 = __commonJS({
start: { height: current + "px" },
end: { height: full + "px" }
}, () => el._x_isShown = true, () => {
if (el.getBoundingClientRect().height == full) {
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
el.style.overflow = null;
}
});
@@ -3943,9 +3933,9 @@ var require_module_cjs2 = __commonJS({
}
});
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js
// ../alpine/packages/focus/dist/module.cjs.js
var require_module_cjs3 = __commonJS({
"../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js"(exports, module) {
"../alpine/packages/focus/dist/module.cjs.js"(exports, module) {
var __create2 = Object.create;
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -4945,9 +4935,9 @@ var require_module_cjs3 = __commonJS({
}
});
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js
// ../alpine/packages/persist/dist/module.cjs.js
var require_module_cjs4 = __commonJS({
"../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js"(exports, module) {
"../alpine/packages/persist/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
@@ -5034,9 +5024,9 @@ var require_module_cjs4 = __commonJS({
}
});
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js
// ../alpine/packages/intersect/dist/module.cjs.js
var require_module_cjs5 = __commonJS({
"../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js"(exports, module) {
"../alpine/packages/intersect/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
@@ -5116,80 +5106,8 @@ var require_module_cjs5 = __commonJS({
}
});
// node_modules/@alpinejs/resize/dist/module.cjs.js
var require_module_cjs6 = __commonJS({
"node_modules/@alpinejs/resize/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __export = (target, all2) => {
for (var name in all2)
__defProp2(target, name, { get: all2[name], enumerable: true });
};
var __copyProps2 = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames2(from))
if (!__hasOwnProp2.call(to, key) && key !== except)
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
var module_exports = {};
__export(module_exports, {
default: () => module_default,
resize: () => src_default
});
module.exports = __toCommonJS(module_exports);
function src_default(Alpine19) {
Alpine19.directive("resize", Alpine19.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
let evaluator = evaluateLater(expression);
let evaluate = (width, height) => {
evaluator(() => {
}, { scope: { "$width": width, "$height": height } });
};
let off = modifiers.includes("document") ? onDocumentResize(evaluate) : onElResize(el, evaluate);
cleanup(() => off());
}));
}
function onElResize(el, callback) {
let observer = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries);
callback(width, height);
});
observer.observe(el);
return () => observer.disconnect();
}
var documentResizeObserver;
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
function onDocumentResize(callback) {
documentResizeObserverCallbacks.add(callback);
if (!documentResizeObserver) {
documentResizeObserver = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries);
documentResizeObserverCallbacks.forEach((i) => i(width, height));
});
documentResizeObserver.observe(document.documentElement);
}
return () => {
documentResizeObserverCallbacks.delete(callback);
};
}
function dimensions(entries) {
let width, height;
for (let entry of entries) {
width = entry.borderBoxSize[0].inlineSize;
height = entry.borderBoxSize[0].blockSize;
}
return [width, height];
}
var module_default = src_default;
}
});
// ../alpine/packages/anchor/dist/module.cjs.js
var require_module_cjs7 = __commonJS({
var require_module_cjs6 = __commonJS({
"../alpine/packages/anchor/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -6727,7 +6645,7 @@ var require_nprogress = __commonJS({
});
// ../alpine/packages/morph/dist/module.cjs.js
var require_module_cjs8 = __commonJS({
var require_module_cjs7 = __commonJS({
"../alpine/packages/morph/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -7088,9 +7006,9 @@ var require_module_cjs8 = __commonJS({
}
});
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js
var require_module_cjs9 = __commonJS({
"../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.cjs.js"(exports, module) {
// ../alpine/packages/mask/dist/module.cjs.js
var require_module_cjs8 = __commonJS({
"../alpine/packages/mask/dist/module.cjs.js"(exports, module) {
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
@@ -8591,8 +8509,7 @@ var import_collapse = __toESM(require_module_cjs2());
var import_focus = __toESM(require_module_cjs3());
var import_persist2 = __toESM(require_module_cjs4());
var import_intersect = __toESM(require_module_cjs5());
var import_resize = __toESM(require_module_cjs6());
var import_anchor = __toESM(require_module_cjs7());
var import_anchor = __toESM(require_module_cjs6());
// js/plugins/navigate/history.js
var Snapshot = class {
@@ -8743,7 +8660,7 @@ function extractDestinationFromLink(linkEl) {
return createUrlObjectFromString(linkEl.getAttribute("href"));
}
function createUrlObjectFromString(urlString) {
return urlString !== null && new URL(urlString, document.baseURI);
return new URL(urlString, document.baseURI);
}
function getUriStringFromUrlObject(urlObject) {
return urlObject.pathname + urlObject.search + urlObject.hash;
@@ -8865,10 +8782,8 @@ function restoreScrollPositionOrScrollToTop() {
}
};
queueMicrotask(() => {
queueMicrotask(() => {
scroll(document.body);
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
});
scroll(document.body);
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
});
}
@@ -9017,44 +8932,6 @@ function injectStyles() {
document.head.appendChild(style);
}
// js/plugins/navigate/popover.js
function packUpPersistedPopovers(persistedEl) {
persistedEl.querySelectorAll(":popover-open").forEach((el) => {
el.setAttribute("data-navigate-popover-open", "");
let animations = el.getAnimations();
el._pausedAnimations = animations.map((animation) => ({
keyframes: animation.effect.getKeyframes(),
options: {
duration: animation.effect.getTiming().duration,
easing: animation.effect.getTiming().easing,
fill: animation.effect.getTiming().fill,
iterations: animation.effect.getTiming().iterations
},
currentTime: animation.currentTime,
playState: animation.playState
}));
animations.forEach((i) => i.pause());
});
}
function unPackPersistedPopovers(persistedEl) {
persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => {
el.removeAttribute("data-navigate-popover-open");
queueMicrotask(() => {
if (!el.isConnected)
return;
el.showPopover();
el.getAnimations().forEach((i) => i.finish());
if (el._pausedAnimations) {
el._pausedAnimations.forEach(({ keyframes, options, currentTime, now, playState }) => {
let animation = el.animate(keyframes, options);
animation.currentTime = currentTime;
});
delete el._pausedAnimations;
}
});
});
}
// js/plugins/navigate/page.js
var oldBodyScriptTagHashes = [];
var attributesExemptFromScriptTagHashing = [
@@ -9193,7 +9070,7 @@ var autofocus = false;
function navigate_default(Alpine19) {
Alpine19.navigate = (url) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: false,
cached: false
@@ -9210,21 +9087,17 @@ function navigate_default(Alpine19) {
let shouldPrefetchOnHover = modifiers.includes("hover");
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
let destination = extractDestinationFromLink(el);
if (!destination)
return;
prefetchHtml(destination, (html, finalDestination) => {
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
});
});
whenThisLinkIsPressed(el, (whenItIsReleased) => {
let destination = extractDestinationFromLink(el);
if (!destination)
return;
prefetchHtml(destination, (html, finalDestination) => {
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
});
whenItIsReleased(() => {
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: false,
cached: false
@@ -9238,7 +9111,7 @@ function navigate_default(Alpine19) {
function navigateTo(destination, shouldPushToHistoryState = true) {
showProgressBar && showAndStartProgressBar();
fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => {
fireEventForOtherLibrariesToHookInto("alpine:navigating");
fireEventForOtherLibariesToHookInto("alpine:navigating");
restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway();
showProgressBar && finishAndHideProgressBar();
cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement();
@@ -9246,7 +9119,6 @@ function navigate_default(Alpine19) {
preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => {
enablePersist && storePersistantElementsForLater((persistedEl) => {
packUpPersistedTeleports(persistedEl);
packUpPersistedPopovers(persistedEl);
});
if (shouldPushToHistoryState) {
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
@@ -9257,7 +9129,6 @@ function navigate_default(Alpine19) {
removeAnyLeftOverStaleTeleportTargets(document.body);
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
unPackPersistedTeleports(persistedEl);
unPackPersistedPopovers(persistedEl);
});
restoreScrollPositionOrScrollToTop();
afterNewScriptsAreDoneLoading(() => {
@@ -9266,7 +9137,7 @@ function navigate_default(Alpine19) {
autofocus && autofocusElementsWithTheAutofocusAttribute();
});
nowInitializeAlpineOnTheNewPage(Alpine19);
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
});
});
@@ -9276,7 +9147,7 @@ function navigate_default(Alpine19) {
whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => {
ifThePageBeingVisitedHasntBeenCached((url) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: true,
cached: false
@@ -9288,7 +9159,7 @@ function navigate_default(Alpine19) {
});
}, (html, url, currentPageUrl, currentPageKey) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: true,
cached: true
@@ -9296,31 +9167,29 @@ function navigate_default(Alpine19) {
if (prevented)
return;
storeScrollInformationInHtmlBeforeNavigatingAway();
fireEventForOtherLibrariesToHookInto("alpine:navigating");
fireEventForOtherLibariesToHookInto("alpine:navigating");
updateCurrentPageHtmlInSnapshotCacheForLaterBackButtonClicks(currentPageUrl, currentPageKey);
preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => {
enablePersist && storePersistantElementsForLater((persistedEl) => {
packUpPersistedTeleports(persistedEl);
packUpPersistedPopovers(persistedEl);
});
swapCurrentPageWithNewHtml(html, () => {
removeAnyLeftOverStaleProgressBars();
removeAnyLeftOverStaleTeleportTargets(document.body);
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
unPackPersistedTeleports(persistedEl);
unPackPersistedPopovers(persistedEl);
});
restoreScrollPositionOrScrollToTop();
andAfterAllThis(() => {
autofocus && autofocusElementsWithTheAutofocusAttribute();
nowInitializeAlpineOnTheNewPage(Alpine19);
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
});
});
});
setTimeout(() => {
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
}
function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) {
@@ -9337,7 +9206,7 @@ function preventAlpineFromPickingUpDomChanges(Alpine19, callback) {
});
});
}
function fireEventForOtherLibrariesToHookInto(name, detail) {
function fireEventForOtherLibariesToHookInto(name, detail) {
let event = new CustomEvent(name, {
cancelable: true,
bubbles: true,
@@ -9572,8 +9441,8 @@ function fromQueryString(search) {
}
// js/lifecycle.js
var import_morph = __toESM(require_module_cjs8());
var import_mask = __toESM(require_module_cjs9());
var import_morph = __toESM(require_module_cjs7());
var import_mask = __toESM(require_module_cjs8());
var import_alpinejs5 = __toESM(require_module_cjs());
function start() {
setTimeout(() => ensureLivewireScriptIsntMisplaced());
@@ -9582,7 +9451,6 @@ function start() {
import_alpinejs5.default.plugin(import_morph.default);
import_alpinejs5.default.plugin(history2);
import_alpinejs5.default.plugin(import_intersect.default);
import_alpinejs5.default.plugin(import_resize.default);
import_alpinejs5.default.plugin(import_collapse.default);
import_alpinejs5.default.plugin(import_anchor.default);
import_alpinejs5.default.plugin(import_focus.default);
@@ -9853,7 +9721,6 @@ function morph2(component, el, html) {
},
lookahead: false
});
trigger("morphed", { el, component });
}
function isntElement(el) {
return typeof el.hasAttribute !== "function";
@@ -10923,4 +10790,3 @@ focus-trap/dist/focus-trap.js:
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
*)
*/
//# sourceMappingURL=livewire.esm.js.map
+93 -197
View File
@@ -851,9 +851,10 @@
});
}
function cleanupElement(el) {
el._x_effects?.forEach(dequeueJob);
while (el._x_cleanups?.length)
el._x_cleanups.pop()();
if (el._x_cleanups) {
while (el._x_cleanups.length)
el._x_cleanups.pop()();
}
}
var observer = new MutationObserver(onMutate);
var currentlyObserving = false;
@@ -1091,23 +1092,27 @@
magics[name] = callback;
}
function injectMagics(obj, el) {
let memoizedUtilities = getUtilities(el);
Object.entries(magics).forEach(([name, callback]) => {
let memoizedUtilities = null;
function getUtilities() {
if (memoizedUtilities) {
return memoizedUtilities;
} else {
let [utilities, cleanup2] = getElementBoundUtilities(el);
memoizedUtilities = { interceptor, ...utilities };
onElRemoved(el, cleanup2);
return memoizedUtilities;
}
}
Object.defineProperty(obj, `$${name}`, {
get() {
return callback(el, memoizedUtilities);
return callback(el, getUtilities());
},
enumerable: false
});
});
return obj;
}
function getUtilities(el) {
let [utilities, cleanup2] = getElementBoundUtilities(el);
let utils = { interceptor, ...utilities };
onElRemoved(el, cleanup2);
return utils;
}
function tryCatch(el, expression, callback, ...args) {
try {
return callback(...args);
@@ -1481,8 +1486,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
function destroyTree(root, walker = walk) {
walker(root, (el) => {
cleanupElement(el);
cleanupAttributes(el);
cleanupElement(el);
});
}
function warnAboutMissingPlugins() {
@@ -1975,7 +1980,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
}
function bindInputValue(el, value) {
if (isRadio(el)) {
if (el.type === "radio") {
if (el.attributes.value === void 0) {
el.value = value;
}
@@ -1986,7 +1991,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
el.checked = checkedAttrLooseCompare(el.value, value);
}
}
} else if (isCheckbox(el)) {
} else if (el.type === "checkbox") {
if (Number.isInteger(value)) {
el.value = value;
} else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) {
@@ -2062,37 +2067,34 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
return rawValue ? Boolean(rawValue) : null;
}
var booleanAttributes = /* @__PURE__ */ new Set([
"allowfullscreen",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"inert",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected",
"shadowrootclonable",
"shadowrootdelegatesfocus",
"shadowrootserializable"
]);
function isBooleanAttr(attrName) {
return booleanAttributes.has(attrName);
const booleanAttributes = [
"disabled",
"checked",
"required",
"readonly",
"open",
"selected",
"autofocus",
"itemscope",
"multiple",
"novalidate",
"allowfullscreen",
"allowpaymentrequest",
"formnovalidate",
"autoplay",
"controls",
"loop",
"muted",
"playsinline",
"default",
"ismap",
"reversed",
"async",
"defer",
"nomodule"
];
return booleanAttributes.includes(attrName);
}
function attributeShouldntBePreservedIfFalsy(name) {
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
@@ -2125,12 +2127,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
return attr;
}
function isCheckbox(el) {
return el.type === "checkbox" || el.localName === "ui-checkbox" || el.localName === "ui-switch";
}
function isRadio(el) {
return el.type === "radio" || el.localName === "ui-radio";
}
function debounce(func, wait) {
var timeout;
return function() {
@@ -2199,10 +2195,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
return stores[name];
}
stores[name] = value;
initInterceptors(stores[name]);
if (typeof value === "object" && value !== null && value.hasOwnProperty("init") && typeof value.init === "function") {
stores[name].init();
}
initInterceptors(stores[name]);
}
function getStores() {
return stores;
@@ -2284,7 +2280,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
get raw() {
return raw;
},
version: "3.14.3",
version: "3.14.1",
flushAndStopDeferringMutations,
dontAutoEvaluateFunctions,
disableEffectScheduling,
@@ -3140,10 +3136,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
placeInDom(el._x_teleport, target2, modifiers);
});
};
cleanup2(() => mutateDom(() => {
clone2.remove();
destroyTree(clone2);
}));
cleanup2(() => clone2.remove());
});
var teleportContainerDuringClone = document.createElement("div");
function getTarget(expression) {
@@ -3367,7 +3360,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
setValue(getInputValue(el, modifiers, e, getValue()));
});
if (modifiers.includes("fill")) {
if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
setValue(getInputValue(el, modifiers, { target: el }, getValue()));
}
}
@@ -3407,7 +3400,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
return mutateDom(() => {
if (event instanceof CustomEvent && event.detail !== void 0)
return event.detail !== null && event.detail !== void 0 ? event.detail : event.target.value;
else if (isCheckbox(el)) {
else if (el.type === "checkbox") {
if (Array.isArray(currentValue)) {
let newValue = null;
if (modifiers.includes("number")) {
@@ -3438,7 +3431,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
} else {
let newValue;
if (isRadio(el)) {
if (el.type === "radio") {
if (event.target.checked) {
newValue = event.target.value;
} else {
@@ -3631,10 +3624,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
el._x_lookup = {};
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
cleanup2(() => {
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
destroyTree(el2);
el2.remove();
}));
Object.values(el._x_lookup).forEach((el2) => el2.remove());
delete el._x_prevKeys;
delete el._x_lookup;
});
@@ -3703,12 +3693,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
for (let i = 0; i < removes.length; i++) {
let key = removes[i];
if (!(key in lookup))
continue;
mutateDom(() => {
destroyTree(lookup[key]);
lookup[key].remove();
});
if (!!lookup[key]._x_effects) {
lookup[key]._x_effects.forEach(dequeueJob);
}
lookup[key].remove();
lookup[key] = null;
delete lookup[key];
}
for (let i = 0; i < moves.length; i++) {
@@ -3829,10 +3818,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
el._x_currentIfEl = clone2;
el._x_undoIf = () => {
mutateDom(() => {
destroyTree(clone2);
clone2.remove();
walk(clone2, (node) => {
if (!!node._x_effects) {
node._x_effects.forEach(dequeueJob);
}
});
clone2.remove();
delete el._x_currentIfEl;
};
return clone2;
@@ -4771,7 +4762,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
};
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.esm.js
// ../alpine/packages/collapse/dist/module.esm.js
function src_default2(Alpine3) {
Alpine3.directive("collapse", collapse);
collapse.inline = (el, { modifiers }) => {
@@ -4821,7 +4812,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
start: { height: current + "px" },
end: { height: full + "px" }
}, () => el._x_isShown = true, () => {
if (el.getBoundingClientRect().height == full) {
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
el.style.overflow = null;
}
});
@@ -4865,7 +4856,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
var module_default2 = src_default2;
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.esm.js
// ../alpine/packages/focus/dist/module.esm.js
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"];
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
var NoElement = typeof Element === "undefined";
@@ -4977,11 +4968,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
var checked = getCheckedRadio(radioSet, node.form);
return !checked || checked === node;
};
var isRadio2 = function isRadio22(node) {
var isRadio = function isRadio2(node) {
return isInput(node) && node.type === "radio";
};
var isNonTabbableRadio = function isNonTabbableRadio2(node) {
return isRadio2(node) && !isTabbableRadio(node);
return isRadio(node) && !isTabbableRadio(node);
};
var isZeroArea = function isZeroArea2(node) {
var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;
@@ -5814,7 +5805,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
var module_default3 = src_default3;
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.esm.js
// ../alpine/packages/persist/dist/module.esm.js
function src_default4(Alpine3) {
let persist = () => {
let alias;
@@ -5876,7 +5867,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
var module_default4 = src_default4;
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.esm.js
// ../alpine/packages/intersect/dist/module.esm.js
function src_default5(Alpine3) {
Alpine3.directive("intersect", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
let evaluate3 = evaluateLater2(expression);
@@ -5931,51 +5922,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
var module_default5 = src_default5;
// node_modules/@alpinejs/resize/dist/module.esm.js
function src_default6(Alpine3) {
Alpine3.directive("resize", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
let evaluator = evaluateLater2(expression);
let evaluate3 = (width, height) => {
evaluator(() => {
}, { scope: { "$width": width, "$height": height } });
};
let off = modifiers.includes("document") ? onDocumentResize(evaluate3) : onElResize(el, evaluate3);
cleanup2(() => off());
}));
}
function onElResize(el, callback) {
let observer2 = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries);
callback(width, height);
});
observer2.observe(el);
return () => observer2.disconnect();
}
var documentResizeObserver;
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
function onDocumentResize(callback) {
documentResizeObserverCallbacks.add(callback);
if (!documentResizeObserver) {
documentResizeObserver = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries);
documentResizeObserverCallbacks.forEach((i) => i(width, height));
});
documentResizeObserver.observe(document.documentElement);
}
return () => {
documentResizeObserverCallbacks.delete(callback);
};
}
function dimensions(entries) {
let width, height;
for (let entry of entries) {
width = entry.borderBoxSize[0].inlineSize;
height = entry.borderBoxSize[0].blockSize;
}
return [width, height];
}
var module_default6 = src_default6;
// ../alpine/packages/anchor/dist/module.esm.js
var min = Math.min;
var max = Math.max;
@@ -7150,7 +7096,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
platform: platformWithCache
});
};
function src_default7(Alpine3) {
function src_default6(Alpine3) {
Alpine3.magic("anchor", (el) => {
if (!el._x_anchor)
throw "Alpine: No x-anchor directive found on element using $anchor...";
@@ -7208,7 +7154,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
let unstyled = modifiers.includes("no-style");
return { placement, offsetValue, unstyled };
}
var module_default7 = src_default7;
var module_default6 = src_default6;
// js/plugins/navigate/history.js
var Snapshot = class {
@@ -7359,7 +7305,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
return createUrlObjectFromString(linkEl.getAttribute("href"));
}
function createUrlObjectFromString(urlString) {
return urlString !== null && new URL(urlString, document.baseURI);
return new URL(urlString, document.baseURI);
}
function getUriStringFromUrlObject(urlObject) {
return urlObject.pathname + urlObject.search + urlObject.hash;
@@ -7480,10 +7426,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
}
};
queueMicrotask(() => {
queueMicrotask(() => {
scroll(document.body);
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
});
scroll(document.body);
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
});
}
@@ -7631,44 +7575,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
document.head.appendChild(style);
}
// js/plugins/navigate/popover.js
function packUpPersistedPopovers(persistedEl) {
persistedEl.querySelectorAll(":popover-open").forEach((el) => {
el.setAttribute("data-navigate-popover-open", "");
let animations = el.getAnimations();
el._pausedAnimations = animations.map((animation) => ({
keyframes: animation.effect.getKeyframes(),
options: {
duration: animation.effect.getTiming().duration,
easing: animation.effect.getTiming().easing,
fill: animation.effect.getTiming().fill,
iterations: animation.effect.getTiming().iterations
},
currentTime: animation.currentTime,
playState: animation.playState
}));
animations.forEach((i) => i.pause());
});
}
function unPackPersistedPopovers(persistedEl) {
persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => {
el.removeAttribute("data-navigate-popover-open");
queueMicrotask(() => {
if (!el.isConnected)
return;
el.showPopover();
el.getAnimations().forEach((i) => i.finish());
if (el._pausedAnimations) {
el._pausedAnimations.forEach(({ keyframes, options, currentTime, now, playState }) => {
let animation = el.animate(keyframes, options);
animation.currentTime = currentTime;
});
delete el._pausedAnimations;
}
});
});
}
// js/plugins/navigate/page.js
var oldBodyScriptTagHashes = [];
var attributesExemptFromScriptTagHashing = [
@@ -7807,7 +7713,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
function navigate_default(Alpine3) {
Alpine3.navigate = (url) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: false,
cached: false
@@ -7824,21 +7730,17 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
let shouldPrefetchOnHover = modifiers.includes("hover");
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
let destination = extractDestinationFromLink(el);
if (!destination)
return;
prefetchHtml(destination, (html, finalDestination) => {
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
});
});
whenThisLinkIsPressed(el, (whenItIsReleased) => {
let destination = extractDestinationFromLink(el);
if (!destination)
return;
prefetchHtml(destination, (html, finalDestination) => {
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
});
whenItIsReleased(() => {
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: false,
cached: false
@@ -7852,7 +7754,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
function navigateTo(destination, shouldPushToHistoryState = true) {
showProgressBar && showAndStartProgressBar();
fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => {
fireEventForOtherLibrariesToHookInto("alpine:navigating");
fireEventForOtherLibariesToHookInto("alpine:navigating");
restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway();
showProgressBar && finishAndHideProgressBar();
cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement();
@@ -7860,7 +7762,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => {
enablePersist && storePersistantElementsForLater((persistedEl) => {
packUpPersistedTeleports(persistedEl);
packUpPersistedPopovers(persistedEl);
});
if (shouldPushToHistoryState) {
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
@@ -7871,7 +7772,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
removeAnyLeftOverStaleTeleportTargets(document.body);
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
unPackPersistedTeleports(persistedEl);
unPackPersistedPopovers(persistedEl);
});
restoreScrollPositionOrScrollToTop();
afterNewScriptsAreDoneLoading(() => {
@@ -7880,7 +7780,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
autofocus && autofocusElementsWithTheAutofocusAttribute();
});
nowInitializeAlpineOnTheNewPage(Alpine3);
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
});
});
@@ -7890,7 +7790,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => {
ifThePageBeingVisitedHasntBeenCached((url) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: true,
cached: false
@@ -7902,7 +7802,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
}, (html, url, currentPageUrl, currentPageKey) => {
let destination = createUrlObjectFromString(url);
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
url: destination,
history: true,
cached: true
@@ -7910,31 +7810,29 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
if (prevented)
return;
storeScrollInformationInHtmlBeforeNavigatingAway();
fireEventForOtherLibrariesToHookInto("alpine:navigating");
fireEventForOtherLibariesToHookInto("alpine:navigating");
updateCurrentPageHtmlInSnapshotCacheForLaterBackButtonClicks(currentPageUrl, currentPageKey);
preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => {
enablePersist && storePersistantElementsForLater((persistedEl) => {
packUpPersistedTeleports(persistedEl);
packUpPersistedPopovers(persistedEl);
});
swapCurrentPageWithNewHtml(html, () => {
removeAnyLeftOverStaleProgressBars();
removeAnyLeftOverStaleTeleportTargets(document.body);
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
unPackPersistedTeleports(persistedEl);
unPackPersistedPopovers(persistedEl);
});
restoreScrollPositionOrScrollToTop();
andAfterAllThis(() => {
autofocus && autofocusElementsWithTheAutofocusAttribute();
nowInitializeAlpineOnTheNewPage(Alpine3);
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
});
});
});
setTimeout(() => {
fireEventForOtherLibrariesToHookInto("alpine:navigated");
fireEventForOtherLibariesToHookInto("alpine:navigated");
});
}
function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) {
@@ -7951,7 +7849,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
});
}
function fireEventForOtherLibrariesToHookInto(name, detail) {
function fireEventForOtherLibariesToHookInto(name, detail) {
let event = new CustomEvent(name, {
cancelable: true,
bubbles: true,
@@ -8515,13 +8413,13 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
to.setAttribute("id", fromId);
to.id = fromId;
}
function src_default8(Alpine3) {
function src_default7(Alpine3) {
Alpine3.morph = morph;
}
var module_default8 = src_default8;
var module_default7 = src_default7;
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.esm.js
function src_default9(Alpine3) {
// ../alpine/packages/mask/dist/module.esm.js
function src_default8(Alpine3) {
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
let templateFn = () => expression;
let lastInputValue = "";
@@ -8683,23 +8581,22 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
});
return template;
}
var module_default9 = src_default9;
var module_default8 = src_default8;
// js/lifecycle.js
function start2() {
setTimeout(() => ensureLivewireScriptIsntMisplaced());
dispatch(document, "livewire:init");
dispatch(document, "livewire:initializing");
module_default.plugin(module_default8);
module_default.plugin(module_default7);
module_default.plugin(history2);
module_default.plugin(module_default5);
module_default.plugin(module_default6);
module_default.plugin(module_default2);
module_default.plugin(module_default7);
module_default.plugin(module_default6);
module_default.plugin(module_default3);
module_default.plugin(module_default4);
module_default.plugin(navigate_default);
module_default.plugin(module_default9);
module_default.plugin(module_default8);
module_default.addRootSelector(() => "[wire\\:id]");
module_default.onAttributesAdded((el, attributes) => {
if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
@@ -8958,7 +8855,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
},
lookahead: false
});
trigger2("morphed", { el, component });
}
function isntElement(el) {
return typeof el.hasAttribute !== "function";
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,2 +1,2 @@
{"/livewire.js":"38dc8241"}
{"/livewire.js":"87e1046f"}
+8 -10
View File
@@ -621,30 +621,31 @@ h4 {
//border-left: 1px solid #dddddd;
//border-right: 1px solid #dddddd;
display: table;
}
.row-striped .row:nth-of-type(odd) div {
background-color: #f9f9f9;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-striped .row:nth-of-type(even) div {
background: #FFFFFF;
border-top: 1px solid #dddddd;
display: table-cell;
word-wrap: break-word;
}
.row-new-striped {
vertical-align: top;
padding: 3px;
line-height: 2.6;
padding: 0px;
margin-left: 20px;
display: table;
width: 100%;
word-wrap: break-word;
table-layout:fixed;
padding-right: 20px;
}
/**
@@ -655,28 +656,25 @@ h4 {
.row-new-striped > .row:nth-of-type(even) {
background: #FFFFFF;
border-top: 1px solid #dddddd;
line-height: 1.9;
display: table-row;
}
.row-new-striped > .row:nth-of-type(odd) {
background-color: #F8F8F8;
border-top: 1px solid #dddddd;
display: table-row;
line-height: 1.9;
padding: 2px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
.row-new-striped div {
display: table-cell;
border-top: 1px solid #dddddd;
padding: 6px;
}
@@ -14,8 +14,6 @@ return [
'error' => 'Asset was not created, please try again. :(',
'success' => 'Asset created successfully. :)',
'success_linked' => 'Asset with tag :tag was created successfully. <strong><a href=":link" style="color: white;">Click here to view</a></strong>.',
'multi_success_linked' => 'Asset with tag :links was created successfully.|:count assets were created succesfully. :links.',
'partial_failure' => 'An asset was unable to be created. Reason: :failures|:count assets were unable to be created. Reasons: :failures',
],
'update' => [
@@ -81,11 +79,6 @@ return [
'no_assets_selected' => 'You must select at least one asset from the list',
],
'multi-checkout' => [
'error' => 'Asset was not checked out, please try again|Assets were not checked out, please try again',
'success' => 'Asset checked out successfully.|Assets checked out successfully.',
],
'checkin' => [
'error' => 'Asset was not checked in, please try again',
'success' => 'Asset checked in successfully.',
@@ -385,6 +385,5 @@ return [
'restore_default_avatar_help' => '',
'due_checkin_days' => 'Due For Checkin Warning',
'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?',
'no_groups' => 'No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.',
];
+1 -2
View File
@@ -230,7 +230,7 @@ return [
'purchase_date' => 'Purchase Date',
'qty' => 'QTY',
'quantity' => 'Quantity',
'quantity_minimum' => 'You have one item below or almost below minimum quantity levels|You have :count items below or almost below minimum quantity levels',
'quantity_minimum' => 'You have :count items below or almost below minimum quantity levels',
'quickscan_checkin' => 'Quick Scan Checkin',
'quickscan_checkin_status' => 'Checkin Status',
'ready_to_deploy' => 'Ready to Deploy',
@@ -434,7 +434,6 @@ return [
'alt_uploaded_image_thumbnail' => 'Uploaded thumbnail',
'placeholder_kit' => 'Select a kit',
'file_not_found' => 'File not found',
'log_record_not_found' => 'No record for that log entry was found.',
'preview_not_available' => '(no preview)',
'setup' => 'Setup',
'pre_flight' => 'Pre-Flight',
+1 -9
View File
@@ -173,7 +173,6 @@ return [
'ulid' => 'The :attribute field must be a valid ULID.',
'uuid' => 'The :attribute field must be a valid UUID.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
@@ -195,7 +194,7 @@ return [
'custom_field_not_found_on_model' => 'This field seems to exist, but is not available on this Asset Model\'s fieldset.',
// date_format validation with slightly less stupid messages. It duplicates a lot, but it gets the job done :(
// We use this because the default error message for date_format reflects php Y-m-d, which non-PHP
// We use this because the default error message for date_format is reflects php Y-m-d, which non-PHP
// people won't know how to format.
'purchase_date.date_format' => 'The :attribute must be a valid date in YYYY-MM-DD format',
'last_audit_date.date_format' => 'The :attribute must be a valid date in YYYY-MM-DD hh:mm:ss format',
@@ -207,13 +206,6 @@ return [
'checkboxes' => ':attribute contains invalid options.',
'radio_buttons' => ':attribute is invalid.',
'invalid_value_in_field' => 'Invalid value included in this field',
'ldap_username_field' => [
'not_in' => '<code>sAMAccountName</code> (mixed case) will likely not work. You should use <code>samaccountname</code> (lowercase) instead.'
],
'ldap_auth_filter_query' => ['not_in' => '<code>uid=samaccountname</code> is probably not a valid auth filter. You probably want <code>uid=</code> '],
'ldap_filter' => ['regex' => 'This value should probably not be wrapped in parentheses.'],
],
/*
|--------------------------------------------------------------------------

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