Compare commits
202 Commits
csv_import
...
refactor_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53330514ac | ||
|
|
ba226d9ba3 | ||
|
|
95d136284d | ||
|
|
f5aea7b0d5 | ||
|
|
dfc63641dc | ||
|
|
07a51ec8b1 | ||
|
|
e0ec6795b5 | ||
|
|
5509d756b7 | ||
|
|
b16a978f1b | ||
|
|
55ba6279a4 | ||
|
|
a9eea830e3 | ||
|
|
af564935d5 | ||
|
|
3ee76be7e3 | ||
|
|
d58f87862c | ||
|
|
0b6859c491 | ||
|
|
727c0e458c | ||
|
|
870dc747db | ||
|
|
0fb3d83fac | ||
|
|
0d59ccd6a6 | ||
|
|
5da3ce3564 | ||
|
|
c3bbca30ad | ||
|
|
37f14fff3b | ||
|
|
eb6c51fabd | ||
|
|
5ecd2b6293 | ||
|
|
dccb788a88 | ||
|
|
d10fe77ee7 | ||
|
|
5e1d792bba | ||
|
|
9cf71976f6 | ||
|
|
15745d9737 | ||
|
|
bd97955b9e | ||
|
|
252d99421c | ||
|
|
5767a98ad8 | ||
|
|
0c820cbc0d | ||
|
|
db81701621 | ||
|
|
a05c33febf | ||
|
|
147fcfb8eb | ||
|
|
58a3d09b5f | ||
|
|
30a06a5942 | ||
|
|
6c6af78e08 | ||
|
|
3f79fd7ea7 | ||
|
|
9b06bbb6c3 | ||
|
|
ce30863177 | ||
|
|
d7f70146f4 | ||
|
|
867fa2f36e | ||
|
|
e1882ee6d2 | ||
|
|
7eee239378 | ||
|
|
4188849ae1 | ||
|
|
787e651778 | ||
|
|
ef9b6e3b07 | ||
|
|
06c599cc17 | ||
|
|
6105323877 | ||
|
|
0933a2d4ea | ||
|
|
a8d853c44a | ||
|
|
7e1b47708e | ||
|
|
979e4502ff | ||
|
|
99dd51a965 | ||
|
|
15c2169477 | ||
|
|
50fa6ce335 | ||
|
|
159a1d3f43 | ||
|
|
d1149730be | ||
|
|
46ad1d072f | ||
|
|
780ed91a10 | ||
|
|
bcb4bd9eb4 | ||
|
|
f50ccbcc49 | ||
|
|
0e9b3c9119 | ||
|
|
4933aa5784 | ||
|
|
d67addc69e | ||
|
|
02c80ff18a | ||
|
|
c01190fac2 | ||
|
|
017884f843 | ||
|
|
c49921f50f | ||
|
|
c49abb6aea | ||
|
|
ccd2019448 | ||
|
|
96191a5e93 | ||
|
|
c56affd663 | ||
|
|
3cf746d7df | ||
|
|
6b7af802af | ||
|
|
604a964462 | ||
|
|
2f72c66614 | ||
|
|
fdcc17ca2c | ||
|
|
cba1a56040 | ||
|
|
d9afde4610 | ||
|
|
42095c0167 | ||
|
|
2500375400 | ||
|
|
e4e1d0d50a | ||
|
|
16c8264e76 | ||
|
|
0ae9ce0aa9 | ||
|
|
50b8f180b3 | ||
|
|
914a647204 | ||
|
|
e9225ff3ea | ||
|
|
d0d4159088 | ||
|
|
69b6080bd8 | ||
|
|
b997d728fb | ||
|
|
ddead359d0 | ||
|
|
f3c4e55667 | ||
|
|
0d35335da7 | ||
|
|
feaa714304 | ||
|
|
e1a70023b1 | ||
|
|
de62359c67 | ||
|
|
12bda8fc7b | ||
|
|
fb4fe30049 | ||
|
|
b054017c9f | ||
|
|
524a442724 | ||
|
|
8aa298f6b0 | ||
|
|
1f34657734 | ||
|
|
0856ee648e | ||
|
|
1dafc970df | ||
|
|
94a074a193 | ||
|
|
2d49e1eff2 | ||
|
|
705bc6f0c0 | ||
|
|
b5b93fdd3a | ||
|
|
d9432baf7a | ||
|
|
90be2a4498 | ||
|
|
3886da8941 | ||
|
|
130e0c6242 | ||
|
|
4361a10818 | ||
|
|
aa8048ac15 | ||
|
|
710e738e8e | ||
|
|
3705b91439 | ||
|
|
707bdad192 | ||
|
|
242fe33f97 | ||
|
|
ded79469c1 | ||
|
|
d9fbf330e5 | ||
|
|
2cb9ac26cd | ||
|
|
185bc966e6 | ||
|
|
a7f7e4938f | ||
|
|
2883e79193 | ||
|
|
9c4191ae0a | ||
|
|
3a77b83e9c | ||
|
|
d9be2b5a5e | ||
|
|
69c43c610c | ||
|
|
4f957bcf71 | ||
|
|
5cda7cce48 | ||
|
|
41b94e7128 | ||
|
|
aa55fa6ff4 | ||
|
|
67a605c9a5 | ||
|
|
c2663ea1e0 | ||
|
|
3ee5713740 | ||
|
|
ab8a22f77e | ||
|
|
56e7ea6677 | ||
|
|
26d7572bcc | ||
|
|
8c9132aff9 | ||
|
|
382ebef8ca | ||
|
|
2be88cb955 | ||
|
|
3f36d5f9b3 | ||
|
|
f76da48448 | ||
|
|
8035326675 | ||
|
|
dfdd85abb1 | ||
|
|
063ea1892b | ||
|
|
e213053775 | ||
|
|
88d549e7c5 | ||
|
|
983a25aa5f | ||
|
|
bde05d6ed9 | ||
|
|
b5ffe54bd0 | ||
|
|
863c0a8b60 | ||
|
|
32551d55d7 | ||
|
|
7fc498a597 | ||
|
|
cb281c6408 | ||
|
|
f483cd448f | ||
|
|
78ca1026fd | ||
|
|
722d5a58e7 | ||
|
|
7461c3e0ca | ||
|
|
3c0f4181ae | ||
|
|
3699d79363 | ||
|
|
350b627ce1 | ||
|
|
ee046a8688 | ||
|
|
b34a7c8aad | ||
|
|
f92bf5dc20 | ||
|
|
4d9e85026a | ||
|
|
3153bbb13f | ||
|
|
3dc64cc5e0 | ||
|
|
5b90d79494 | ||
|
|
fdfea390fb | ||
|
|
e807cfab86 | ||
|
|
0b3f458561 | ||
|
|
3f74ff25d2 | ||
|
|
b6340532d7 | ||
|
|
c71411465a | ||
|
|
d609ed50a4 | ||
|
|
d639d6fbc1 | ||
|
|
fff069824b | ||
|
|
a5be18bb14 | ||
|
|
2137890496 | ||
|
|
9b22d6d493 | ||
|
|
86f13a9735 | ||
|
|
a071fff954 | ||
|
|
8b50ef077d | ||
|
|
607f29030f | ||
|
|
a6bcd3c0c2 | ||
|
|
9b293afaac | ||
|
|
636c776620 | ||
|
|
d5f659024c | ||
|
|
832e50a71e | ||
|
|
fa19686248 | ||
|
|
c021609c13 | ||
|
|
7b31df7c14 | ||
|
|
12fd9cd97a | ||
|
|
9e03c4ba6f | ||
|
|
8eb1c487bc | ||
|
|
e835637ef0 | ||
|
|
eb35608bb5 | ||
|
|
3f07f682de |
@@ -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.0.0.0/8
|
||||
APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1,172.16.0.0/12
|
||||
ALLOW_IFRAMING=false
|
||||
REFERRER_POLICY=same-origin
|
||||
ENABLE_CSP=false
|
||||
|
||||
2
.github/workflows/tests-mysql.yml
vendored
2
.github/workflows/tests-mysql.yml
vendored
@@ -76,4 +76,4 @@ jobs:
|
||||
DB_DATABASE: snipeit
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
run: php artisan test --parallel
|
||||
run: php artisan test
|
||||
|
||||
2
.github/workflows/tests-postgres.yml
vendored
2
.github/workflows/tests-postgres.yml
vendored
@@ -74,4 +74,4 @@ jobs:
|
||||
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
|
||||
DB_USERNAME: snipeit
|
||||
DB_PASSWORD: password
|
||||
run: php artisan test --parallel
|
||||
run: php artisan test
|
||||
|
||||
2
.github/workflows/tests-sqlite.yml
vendored
2
.github/workflows/tests-sqlite.yml
vendored
@@ -58,4 +58,4 @@ jobs:
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: sqlite_testing
|
||||
run: php artisan test --parallel
|
||||
run: php artisan test
|
||||
|
||||
@@ -84,7 +84,11 @@ Since the release of the JSON REST API, several third-party developers have been
|
||||
|
||||
### Contributing
|
||||
|
||||
Please see the documentation on [contributing and developing for Snipe-IT](https://snipe-it.readme.io/docs/contributing-overview).
|
||||
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 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.
|
||||
|
||||
|
||||
@@ -137,23 +137,24 @@ class LdapSync extends Command
|
||||
}
|
||||
|
||||
/* Determine which location to assign users to by default. */
|
||||
$location = null; // TODO - this would be better called "$default_location", which is more explicit about its purpose
|
||||
$default_location = null;
|
||||
if ($this->option('location') != '') {
|
||||
if ($location = Location::where('name', '=', $this->option('location'))->first()) {
|
||||
if ($default_location = Location::where('name', '=', $this->option('location'))->first()) {
|
||||
Log::debug('Location name ' . $this->option('location') . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
Log::debug('Importing to '.$default_location->name.' ('.$default_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 ($location = Location::where('id', '=', $location_id)->first()) {
|
||||
if ($default_location = Location::where('id', '=', $location_id)->first()) {
|
||||
Log::debug('Location ID ' . $location_id . ' passed');
|
||||
Log::debug('Importing to ' . $location->name . ' (' . $location->id . ')');
|
||||
Log::debug('Importing to '.$default_location->name.' ('.$default_location->id.')');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (! isset($location)) {
|
||||
if (!isset($default_location)) {
|
||||
Log::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
|
||||
}
|
||||
|
||||
@@ -229,43 +230,44 @@ 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] ?? '';
|
||||
$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`)
|
||||
|
||||
// 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'],
|
||||
// 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'],
|
||||
]);
|
||||
|
||||
$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){
|
||||
@@ -296,7 +298,7 @@ class LdapSync extends Command
|
||||
$user->department_id = $department->id;
|
||||
}
|
||||
if($ldap_map["location"] != null){
|
||||
$user->location_id = $location ? $location->id : null;
|
||||
$user->location_id = $location?->id;
|
||||
}
|
||||
|
||||
if($ldap_map["manager"] != null){
|
||||
@@ -341,38 +343,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
|
||||
@@ -385,44 +387,47 @@ 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;
|
||||
}
|
||||
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.
|
||||
}
|
||||
$location = null;
|
||||
$user->ldap_import = 1;
|
||||
}
|
||||
// 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;
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
$errors .= $err[0];
|
||||
}
|
||||
$item['note'] = $errors;
|
||||
$item['status'] = 'error';
|
||||
if ($user->save()) {
|
||||
$item['note'] = $item['createorupdate'];
|
||||
$item['status'] = 'success';
|
||||
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
|
||||
$user->groups()->attach($ldap_default_group);
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
} else {
|
||||
foreach ($user->getErrors()->getMessages() as $key => $err) {
|
||||
$errors .= $err[0];
|
||||
}
|
||||
$item['note'] = $errors;
|
||||
$item['status'] = 'error';
|
||||
}
|
||||
|
||||
array_push($summary, $item);
|
||||
}
|
||||
|
||||
if ($this->option('summary')) {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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'));
|
||||
@@ -29,6 +30,11 @@ class ObjectImportCommand extends Command
|
||||
*/
|
||||
protected $description = 'Import Items from CSV';
|
||||
|
||||
/**
|
||||
* The progress indicator instance.
|
||||
*/
|
||||
protected ProgressIndicator $progressIndicator;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
@@ -39,8 +45,6 @@ class ObjectImportCommand extends Command
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
private $bar;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
@@ -48,6 +52,8 @@ 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";
|
||||
@@ -61,46 +67,25 @@ 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->comment('======= Importing Items from '.$filename.' =========');
|
||||
$this->progressIndicator->start('======= Importing Items from '.$filename.' =========');
|
||||
|
||||
$importer->import();
|
||||
|
||||
$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('');
|
||||
$this->progressIndicator->finish('Import finished.');
|
||||
}
|
||||
|
||||
public function errorCallback($item, $field, $errorString)
|
||||
public function errorCallback($item, $field, $error)
|
||||
{
|
||||
$this->errors[$item->name][$field] = $errorString;
|
||||
$this->output->write("\x0D\x1B[2K");
|
||||
|
||||
$this->warn('Error: Item: '.$item->name.' failed validation: '.json_encode($error));
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
public function progress($importedItemsCount)
|
||||
{
|
||||
if (! $this->bar) {
|
||||
$this->bar = $this->output->createProgressBar($count);
|
||||
}
|
||||
static $index = 0;
|
||||
$index++;
|
||||
if ($index < $count) {
|
||||
$this->bar->advance();
|
||||
} else {
|
||||
$this->bar->finish();
|
||||
}
|
||||
$this->progressIndicator->advance();
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -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('deployable', 'desc')
|
||||
$statuslabel_list = ['' => trans('general.select_statuslabel')] + Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('status_type', 'desc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
|
||||
return $statuslabel_list;
|
||||
@@ -572,9 +572,9 @@ class Helper
|
||||
*/
|
||||
public static function deployableStatusLabelList()
|
||||
{
|
||||
$statuslabel_list = Statuslabel::where('deployable', '=', '1')->orderBy('default_label', 'desc')
|
||||
$statuslabel_list = Statuslabel::where('status_type', 'deployable')->orderBy('default_label', 'desc')
|
||||
->orderBy('name', 'asc')
|
||||
->orderBy('deployable', 'desc')
|
||||
->orderBy('status_type', 'desc')
|
||||
->pluck('name', 'id')->toArray();
|
||||
|
||||
return $statuslabel_list;
|
||||
@@ -1123,6 +1123,7 @@ 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',
|
||||
@@ -1135,7 +1136,7 @@ class Helper
|
||||
//Text
|
||||
'txt' => 'far fa-file-alt',
|
||||
'rtf' => 'far fa-file-alt',
|
||||
'xml' => 'far fa-file-alt',
|
||||
'xml' => 'fas fa-code',
|
||||
// Misc
|
||||
'pdf' => 'far fa-file-pdf',
|
||||
'lic' => 'far fa-save',
|
||||
@@ -1148,41 +1149,7 @@ 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.
|
||||
|
||||
@@ -7,6 +7,7 @@ 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
|
||||
@@ -25,4 +26,64 @@ 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,50 +106,29 @@ class AccessoriesFilesController extends Controller
|
||||
* @param int $accessoryId
|
||||
* @param int $fileId
|
||||
*/
|
||||
public function show($accessoryId = null, $fileId = null, $download = true) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
|
||||
public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
|
||||
{
|
||||
|
||||
Log::debug('Private filesystem is: '.config('filesystems.default'));
|
||||
$accessory = Accessory::find($accessoryId);
|
||||
|
||||
|
||||
|
||||
// the accessory is valid
|
||||
if (isset($accessory->id)) {
|
||||
if ($accessory = Accessory::find($accessoryId)) {
|
||||
$this->authorize('view', $accessory);
|
||||
$this->authorize('accessories.files', $accessory);
|
||||
|
||||
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'));
|
||||
}
|
||||
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
|
||||
$file = 'private_uploads/accessories/'.$log->filename;
|
||||
|
||||
$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);
|
||||
try {
|
||||
return StorageHelper::showOrDownloadFile($file, $log->filename);
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.log_record_not_found'));
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
|
||||
return redirect()->route('accessories.index')->with('error', trans('general.file_not_found'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,6 @@ 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);
|
||||
@@ -197,9 +196,6 @@ 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);
|
||||
@@ -325,7 +321,7 @@ class AccessoriesController extends Controller
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
$accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if ($accessory_checkout->delete()) {
|
||||
@@ -333,14 +329,6 @@ 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')));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class AssetsController extends Controller
|
||||
if ($action == 'audit') {
|
||||
$action = 'audits';
|
||||
}
|
||||
$filter_non_deprecable_assets = false;
|
||||
$filter_non_depreciable_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_deprecable_assets = true;
|
||||
$filter_non_depreciable_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_deprecable_assets) {
|
||||
$non_deprecable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
|
||||
$assets->InModelList($non_deprecable_models->toArray());
|
||||
if ($filter_non_depreciable_assets) {
|
||||
$non_depreciable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
|
||||
$assets->InModelList($non_depreciable_models->toArray());
|
||||
}
|
||||
|
||||
|
||||
@@ -206,18 +206,14 @@ 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.deployable', '=', 0)
|
||||
->where('status_alias.pending', '=', 1)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
->where('status_alias.status_type', '=', 'pending');
|
||||
});
|
||||
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.deployable', '=', 1)
|
||||
->where('status_alias.pending', '=', 0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
->where('status_alias.status_type', '=', 'deployable');
|
||||
});
|
||||
break;
|
||||
case 'Undeployable':
|
||||
@@ -226,20 +222,15 @@ 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.deployable', '=', 0)
|
||||
->where('status_alias.pending', '=', 0)
|
||||
->where('status_alias.archived', '=', 1);
|
||||
->where('status_alias.status_type', '=', 'archived');
|
||||
});
|
||||
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.deployable', '=', 1)
|
||||
->where('status_alias.pending', '=', 0)
|
||||
->where('status_alias.archived', '=', 0);
|
||||
->where('status_alias.status_type', '=', 'deployable');
|
||||
});
|
||||
|
||||
break;
|
||||
case 'Deployed':
|
||||
// more sad, horrible workarounds for laravel bugs when doing full text searches
|
||||
@@ -256,7 +247,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.archived', '=', 0);
|
||||
->where('status_alias.status_type', '!=', 'archived');
|
||||
});
|
||||
|
||||
// If there is a status ID, don't take show_archived_in_list into consideration
|
||||
@@ -265,6 +256,7 @@ class AssetsController extends Controller
|
||||
$join->on('status_alias.id', '=', 'assets.status_id');
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@@ -395,7 +387,7 @@ class AssetsController extends Controller
|
||||
|
||||
// This may not work for all databases, but it works for MySQL
|
||||
if ($numeric_sort) {
|
||||
$assets->orderByRaw($sort_override . ' * 1 ' . $order);
|
||||
$assets->orderByRaw(DB::getTablePrefix() . 'assets.' . $sort_override . ' * 1 ' . $order);
|
||||
} else {
|
||||
$assets->orderBy($sort_override, $order);
|
||||
}
|
||||
@@ -574,8 +566,8 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
if ($asset->assetstatus->status_label == 'pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->status_label.')';
|
||||
}
|
||||
|
||||
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
|
||||
|
||||
@@ -38,6 +38,7 @@ class ComponentsController extends Controller
|
||||
'name',
|
||||
'min_amt',
|
||||
'order_number',
|
||||
'model_number',
|
||||
'serial',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
@@ -47,7 +48,7 @@ class ComponentsController extends Controller
|
||||
];
|
||||
|
||||
$components = Component::select('components.*')
|
||||
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser');
|
||||
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$components = $components->TextSearch($request->input('search'));
|
||||
@@ -69,6 +70,14 @@ 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'));
|
||||
}
|
||||
@@ -98,6 +107,9 @@ 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,6 +258,8 @@ 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')));
|
||||
@@ -268,6 +270,12 @@ 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'))) {
|
||||
@@ -278,7 +286,8 @@ class ConsumablesController extends Controller
|
||||
// Update the consumable data
|
||||
$consumable->assigned_to = $request->input('assigned_to');
|
||||
|
||||
$consumable->users()->attach($consumable->id,
|
||||
for ($i = 0; $i < $consumable->checkout_qty; $i++) {
|
||||
$consumable->users()->attach($consumable->id,
|
||||
[
|
||||
'consumable_id' => $consumable->id,
|
||||
'created_by' => $user->id,
|
||||
@@ -286,6 +295,8 @@ class ConsumablesController extends Controller
|
||||
'note' => $request->input('note'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ 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('accessories as accessories_count')
|
||||
->withCount('components as components_count');
|
||||
|
||||
if ($request->input('deleted') == 'true') {
|
||||
$manufacturers->onlyTrashed();
|
||||
|
||||
@@ -32,7 +32,8 @@ class StatuslabelsController extends Controller
|
||||
'assets_count',
|
||||
'color',
|
||||
'notes',
|
||||
'default_label'
|
||||
'default_label',
|
||||
'status_type',
|
||||
];
|
||||
|
||||
$statuslabels = Statuslabel::with('adminuser')->withCount('assets as assets_count');
|
||||
@@ -49,13 +50,12 @@ 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 = $statuslabels->Pending();
|
||||
} elseif (strtolower($request->input('status_type')) == 'archived') {
|
||||
$statuslabels = $statuslabels->Archived();
|
||||
} elseif (strtolower($request->input('status_type')) == 'deployable') {
|
||||
$statuslabels = $statuslabels->Deployable();
|
||||
$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');
|
||||
} elseif (strtolower($request->input('status_type')) == 'undeployable') {
|
||||
$statuslabels = $statuslabels->Undeployable();
|
||||
$statuslabels->whereNot('status_type', 'deployable');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,19 +92,11 @@ 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());
|
||||
|
||||
$statusType = Statuslabel::getStatuslabelTypesForDB($request->input('type'));
|
||||
$statuslabel->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
$statuslabel->status_type = $request->input('status_type');
|
||||
$statuslabel->color = $request->input('color');
|
||||
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
|
||||
$statuslabel->default_label = $request->input('default_label', 0);
|
||||
@@ -145,20 +137,12 @@ 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->deployable = $statusType['deployable'];
|
||||
$statuslabel->pending = $statusType['pending'];
|
||||
$statuslabel->archived = $statusType['archived'];
|
||||
$statuslabel->status_type = $request->input('status_type');
|
||||
$statuslabel->color = $request->input('color');
|
||||
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
|
||||
$statuslabel->default_label = $request->input('default_label', 0);
|
||||
@@ -206,7 +190,7 @@ class StatuslabelsController extends Controller
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
|
||||
if (Setting::getSettings()->show_archived_in_list == 0 ) {
|
||||
$statuslabels = Statuslabel::withCount('assets')->where('archived','0')->get();
|
||||
$statuslabels = Statuslabel::withCount('assets')->whereNot('status_type','archived')->get();
|
||||
} else {
|
||||
$statuslabels = Statuslabel::withCount('assets')->get();
|
||||
}
|
||||
@@ -300,7 +284,7 @@ class StatuslabelsController extends Controller
|
||||
public function checkIfDeployable($id) : string
|
||||
{
|
||||
$statuslabel = Statuslabel::findOrFail($id);
|
||||
if ($statuslabel->getStatuslabelType() == 'deployable') {
|
||||
if ($statuslabel->status_type == 'deployable') {
|
||||
return '1';
|
||||
}
|
||||
|
||||
@@ -318,22 +302,22 @@ class StatuslabelsController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('view.selectlists');
|
||||
$statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('deployable', 'desc');
|
||||
$statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('status_type', 'desc');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$statuslabels = $statuslabels->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
|
||||
if ($request->filled('deployable')) {
|
||||
$statuslabels = $statuslabels->where('deployable', '=', '1');
|
||||
$statuslabels = $statuslabels->where('status_type', '=', 'deployable');
|
||||
}
|
||||
|
||||
if ($request->filled('pending')) {
|
||||
$statuslabels = $statuslabels->where('pending', '=', '1');
|
||||
$statuslabels = $statuslabels->where('status_type', '=', 'pending');
|
||||
}
|
||||
|
||||
if ($request->filled('archived')) {
|
||||
$statuslabels = $statuslabels->where('archived', '=', '1');
|
||||
$statuslabels = $statuslabels->where('status_type', '=', 'archived');
|
||||
}
|
||||
|
||||
$statuslabels = $statuslabels->orderBy('name', 'ASC')->paginate(50);
|
||||
|
||||
@@ -14,6 +14,7 @@ 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;
|
||||
@@ -42,13 +43,14 @@ 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',
|
||||
@@ -67,7 +69,6 @@ class UsersController extends Controller
|
||||
'users.state',
|
||||
'users.two_factor_enrolled',
|
||||
'users.two_factor_optin',
|
||||
'users.updated_at',
|
||||
'users.username',
|
||||
'users.zip',
|
||||
'users.remote',
|
||||
@@ -255,6 +256,7 @@ class UsersController extends Controller
|
||||
'groups',
|
||||
'activated',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'two_factor_enrolled',
|
||||
'two_factor_optin',
|
||||
'last_login',
|
||||
@@ -370,6 +372,7 @@ 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')) {
|
||||
@@ -451,6 +454,10 @@ 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,43 +61,30 @@ class AssetFilesController extends Controller
|
||||
*/
|
||||
public function show($assetId = null, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
|
||||
{
|
||||
$asset = Asset::find($assetId);
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
if ($asset = Asset::find($assetId)) {
|
||||
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
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');
|
||||
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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$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);
|
||||
return redirect()->route('hardware.show', ['hardware' => $asset])->with('error', trans('general.log_record_not_found'));
|
||||
}
|
||||
// Prepare the error message
|
||||
$error = trans('admin/hardware/message.does_not_exist', ['id' => $fileId]);
|
||||
|
||||
// Redirect to the hardware management page
|
||||
return redirect()->route('hardware.index')->with('error', $error);
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@ 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;
|
||||
@@ -112,8 +111,10 @@ class AssetsController extends Controller
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$success = false;
|
||||
$successes = [];
|
||||
$failures = [];
|
||||
$serials = $request->input('serials');
|
||||
$asset = null;
|
||||
|
||||
for ($a = 1; $a <= count($asset_tags); $a++) {
|
||||
$asset = new Asset();
|
||||
@@ -200,20 +201,35 @@ 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);
|
||||
}
|
||||
|
||||
$success = true;
|
||||
|
||||
$successes[] = "<a href='" . route('hardware.show', ['hardware' => $asset->id]) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
|
||||
|
||||
} else {
|
||||
$failures[] = join(",", $asset->getErrors()->all());
|
||||
}
|
||||
}
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
|
||||
if ($success) {
|
||||
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)]));
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
@@ -336,7 +352,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->getStatuslabelType() != 'deployable') && ($target = $asset->assignedTo)) {
|
||||
if (($status && $status->status_type != 'deployable') && ($target = $asset->assignedTo)) {
|
||||
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
$asset->assigned_to = null;
|
||||
|
||||
@@ -52,6 +52,10 @@ 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');
|
||||
@@ -571,31 +575,34 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference!
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
|
||||
$checkout_success = $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;
|
||||
$asset->unsetEventDispatcher();
|
||||
$asset->save();
|
||||
// TODO - I don't know why this is being saved without events
|
||||
$asset::withoutEvents(function () use ($asset) {
|
||||
$asset->save();
|
||||
});
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
array_merge_recursive($errors, $asset->getErrors()->toArray());
|
||||
if (!$checkout_success) {
|
||||
$errors = array_merge_recursive($errors, $asset->getErrors()->toArray());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (! $errors) {
|
||||
// Redirect to the new asset page
|
||||
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
|
||||
}
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
|
||||
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors());
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ 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);
|
||||
@@ -150,6 +152,8 @@ 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,40 +112,25 @@ 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 (isset($component->id)) {
|
||||
if ($component = Component::find($componentId)) {
|
||||
$this->authorize('view', $component);
|
||||
$this->authorize('components.files', $component);
|
||||
|
||||
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');
|
||||
}
|
||||
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
|
||||
|
||||
$file = 'private_uploads/components/'.$log->filename;
|
||||
$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);
|
||||
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 (config('filesystems.default') == 'local') { // TODO - is there any way to fix this at the StorageHelper layer?
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
}
|
||||
return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found'));
|
||||
|
||||
}
|
||||
|
||||
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('qty');
|
||||
$quantity = $request->input('checkout_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,6 +100,8 @@ 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,7 +104,6 @@ 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)
|
||||
@@ -116,36 +115,18 @@ 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)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
|
||||
$file = 'private_uploads/consumables/'.$log->filename;
|
||||
|
||||
$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);
|
||||
try {
|
||||
return StorageHelper::showOrDownloadFile($file, $log->filename);
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found'));
|
||||
}
|
||||
}
|
||||
// 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,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* This controller provide the health route for
|
||||
@@ -15,13 +16,35 @@ use Illuminate\Routing\Controller as BaseController;
|
||||
*/
|
||||
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()
|
||||
{
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
]);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,37 +112,19 @@ 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)) {
|
||||
return response('No matching record for that asset/file', 500)
|
||||
->header('Content-Type', 'text/plain');
|
||||
}
|
||||
|
||||
$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);
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
||||
// The log record doesn't exist somehow
|
||||
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found'));
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId]));
|
||||
|
||||
@@ -40,7 +40,7 @@ class ModalController extends Controller
|
||||
$view = view("modals.${type}");
|
||||
|
||||
if ($type == "statuslabel") {
|
||||
$view->with('statuslabel_types', Helper::statusTypeList());
|
||||
$view->with('status_types', Helper::statusTypeList());
|
||||
}
|
||||
if (in_array($type, ['kit-model', 'kit-license', 'kit-consumable', 'kit-accessory'])) {
|
||||
$view->with('kitId', $itemId);
|
||||
|
||||
@@ -194,14 +194,14 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function printInventory() : View
|
||||
{
|
||||
$show_user = auth()->user();
|
||||
$show_users = User::where('id',auth()->user()->id)->get();
|
||||
|
||||
return view('users/print')
|
||||
->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('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('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
@@ -222,7 +222,12 @@ class ProfileController extends Controller
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
}
|
||||
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
try {
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@ 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;
|
||||
@@ -273,20 +278,6 @@ 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.
|
||||
@@ -486,7 +477,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postSecurity(Request $request) : RedirectResponse
|
||||
public function postSecurity(StoreSecuritySettings $request) : RedirectResponse
|
||||
{
|
||||
$this->validate($request, [
|
||||
'pwd_secure_complexity' => 'array',
|
||||
@@ -556,7 +547,7 @@ class SettingsController extends Controller
|
||||
*
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postLocalization(Request $request) : RedirectResponse
|
||||
public function postLocalization(StoreLocalizationSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
@@ -599,7 +590,7 @@ class SettingsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function postAlerts(Request $request) : RedirectResponse
|
||||
public function postAlerts(StoreNotificationSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
@@ -780,7 +771,7 @@ class SettingsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function postLabels(Request $request) : RedirectResponse
|
||||
public function postLabels(StoreLabelSettings $request) : RedirectResponse
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
@@ -859,26 +850,7 @@ class SettingsController extends Controller
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
$groups = Group::pluck('name', 'id');
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return view('settings.ldap', compact('setting', 'groups'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -887,7 +859,7 @@ class SettingsController extends Controller
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function postLdapSettings(Request $request) : RedirectResponse
|
||||
public function postLdapSettings(StoreLdapSettings $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('statuslabel_types', Helper::statusTypeList());
|
||||
->with('status_types', Helper::statusTypeList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,19 +61,11 @@ 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->deployable = $statusType['deployable'];
|
||||
$statusLabel->pending = $statusType['pending'];
|
||||
$statusLabel->archived = $statusType['archived'];
|
||||
$statusLabel->status_type = $request->input('status_type');
|
||||
$statusLabel->color = $request->input('color');
|
||||
$statusLabel->show_in_nav = $request->input('show_in_nav', 0);
|
||||
$statusLabel->default_label = $request->input('default_label', 0);
|
||||
@@ -100,11 +92,7 @@ class StatuslabelsController extends Controller
|
||||
return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$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);
|
||||
return view('statuslabels/edit', compact('item'))->with('status_types', Helper::statusTypeList());;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,17 +109,10 @@ 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->deployable = $statustype['deployable'];
|
||||
$statuslabel->pending = $statustype['pending'];
|
||||
$statuslabel->archived = $statustype['archived'];
|
||||
$statuslabel->status_type = $request->input('status_type');
|
||||
$statuslabel->color = $request->input('color');
|
||||
$statuslabel->show_in_nav = $request->input('show_in_nav', 0);
|
||||
$statuslabel->default_label = $request->input('default_label', 0);
|
||||
|
||||
@@ -7,9 +7,6 @@ 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;
|
||||
|
||||
@@ -116,31 +113,30 @@ class UserFilesController extends Controller
|
||||
public function show($userId = null, $fileId = null)
|
||||
{
|
||||
|
||||
|
||||
if (empty($fileId)) {
|
||||
return redirect()->route('users.show')->with('error', 'Invalid file request');
|
||||
}
|
||||
|
||||
$user = User::find($userId);
|
||||
|
||||
// the license is valid
|
||||
if (isset($user->id)) {
|
||||
if ($user = User::find($userId)) {
|
||||
|
||||
$this->authorize('view', $user);
|
||||
|
||||
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
|
||||
$file = 'private_uploads/users/'.$log->filename;
|
||||
|
||||
// Display the file inline
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download('private_uploads/users/'.$log->filename, $log->filename, $headers);
|
||||
try {
|
||||
return StorageHelper::showOrDownloadFile($file, $log->filename);
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found'));
|
||||
}
|
||||
|
||||
return Storage::download('private_uploads/users/'.$log->filename);
|
||||
}
|
||||
|
||||
return redirect()->route('users.index')->with('error', trans('admin/users/message.log_record_not_found'));
|
||||
// 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'));
|
||||
}
|
||||
|
||||
// Redirect to the user management page if the user doesn't exist
|
||||
|
||||
@@ -53,6 +53,10 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\CheckLocale::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'health' => [
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -69,5 +73,6 @@ class Kernel extends HttpKernel
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'health' => null,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -7,14 +7,19 @@ use Closure;
|
||||
|
||||
class CheckForSetup
|
||||
{
|
||||
|
||||
protected $except = [
|
||||
'_debugbar*',
|
||||
'health'
|
||||
];
|
||||
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
|
||||
/**
|
||||
* This is dumb
|
||||
* @todo Check on removing this, not sure if it's still needed
|
||||
* Skip this middleware for the debugbar and health check
|
||||
*/
|
||||
if ($request->is('_debugbar*')) {
|
||||
if ($request->is($this->except)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -25,7 +30,7 @@ class CheckForSetup
|
||||
return $next($request);
|
||||
}
|
||||
} else {
|
||||
if (! ($request->is('setup*')) && ! ($request->is('.env')) && ! ($request->is('health'))) {
|
||||
if (! ($request->is('setup*')) && ! ($request->is('.env'))) {
|
||||
return redirect(config('app.url').'/setup');
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ 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.
|
||||
$idForCurrentUser = is_int($this->company_id)
|
||||
// The use of is_numeric accounts for 1 and '1'.
|
||||
$idForCurrentUser = is_numeric($this->company_id)
|
||||
? Company::getIdForCurrentUser($this->company_id)
|
||||
: $this->company_id;
|
||||
|
||||
|
||||
41
app/Http/Requests/StoreLabelSettings.php
Normal file
41
app/Http/Requests/StoreLabelSettings.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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
app/Http/Requests/StoreLdapSettings.php
Normal file
38
app/Http/Requests/StoreLdapSettings.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
30
app/Http/Requests/StoreLocalizationSettings.php
Normal file
30
app/Http/Requests/StoreLocalizationSettings.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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',
|
||||
];
|
||||
}
|
||||
}
|
||||
37
app/Http/Requests/StoreNotificationSettings.php
Normal file
37
app/Http/Requests/StoreNotificationSettings.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
35
app/Http/Requests/StoreSecuritySettings.php
Normal file
35
app/Http/Requests/StoreSecuritySettings.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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) {
|
||||
if (count($request_fields) > 0 && $validator->errors()->isEmpty()) {
|
||||
$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,6 +141,8 @@ 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') {
|
||||
@@ -158,7 +160,6 @@ class ActionlogsTransformer
|
||||
[
|
||||
'url' => $file_url,
|
||||
'filename' => $actionlog->filename,
|
||||
'inlineable' => (bool) Helper::show_file_inline($actionlog->filename),
|
||||
] : null,
|
||||
|
||||
'item' => ($actionlog->item) ? [
|
||||
@@ -346,4 +347,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->getStatuslabelType()),
|
||||
'status_type'=> e($assetmaintenance->asset->assetstatus->status_type),
|
||||
'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->admin->getFullNameAttribute())
|
||||
'name'=> e($assetmaintenance->adminuser->present()->fullName())
|
||||
] : null, // legacy to not change the shape of the API
|
||||
'created_by' => ($assetmaintenance->adminuser) ? [
|
||||
'id' => (int) $assetmaintenance->adminuser->id,
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AssetsTransformer
|
||||
{
|
||||
@@ -45,8 +44,8 @@ class AssetsTransformer
|
||||
'status_label' => ($asset->assetstatus) ? [
|
||||
'id' => (int) $asset->assetstatus->id,
|
||||
'name'=> e($asset->assetstatus->name),
|
||||
'status_type'=> e($asset->assetstatus->getStatuslabelType()),
|
||||
'status_meta' => e($asset->present()->statusMeta),
|
||||
'status_type'=> e($asset->assetstatus->status_type),
|
||||
'status_meta' => e($asset->assetstatus->status_type),
|
||||
] : null,
|
||||
'category' => (($asset->model) && ($asset->model->category)) ? [
|
||||
'id' => (int) $asset->model->category->id,
|
||||
|
||||
@@ -38,6 +38,8 @@ 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,6 +36,7 @@ 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,7 +24,8 @@ class StatuslabelsTransformer
|
||||
$array = [
|
||||
'id' => (int) $statuslabel->id,
|
||||
'name' => e($statuslabel->name),
|
||||
'type' => $statuslabel->getStatuslabelType(),
|
||||
'type' => $statuslabel->status_type, // legacy - to be removed in later versions
|
||||
'status_type' => $statuslabel->status_type,
|
||||
'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,
|
||||
|
||||
@@ -18,8 +18,8 @@ class AssetImporter extends ItemImporter
|
||||
|
||||
$this->defaultStatusLabelId = Statuslabel::first()->id;
|
||||
|
||||
if (!is_null(Statuslabel::deployable()->first())) {
|
||||
$this->defaultStatusLabelId = Statuslabel::deployable()->first()->id;
|
||||
if (!is_null(Statuslabel::where('status_type', 'deployable')->first())) {
|
||||
$this->defaultStatusLabelId = Statuslabel::where('status_type', 'deployable')->first()->id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ abstract class Importer
|
||||
* Id of User performing import
|
||||
* @var
|
||||
*/
|
||||
|
||||
protected $created_by;
|
||||
/**
|
||||
* Are we updating items in the import
|
||||
@@ -149,21 +148,28 @@ 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 (&$results) {
|
||||
DB::transaction(function () use ($headerRow) {
|
||||
$importedItemsCount = 0;
|
||||
Model::unguard();
|
||||
$resultsCount = count($results);
|
||||
foreach ($results as $row) {
|
||||
|
||||
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);
|
||||
|
||||
$this->handle($row);
|
||||
|
||||
$importedItemsCount++;
|
||||
|
||||
if ($this->progressCallback) {
|
||||
call_user_func($this->progressCallback, $resultsCount);
|
||||
call_user_func($this->progressCallback, $importedItemsCount);
|
||||
}
|
||||
|
||||
$this->log('------------- Action Summary ----------------');
|
||||
}
|
||||
Model::reguard();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -236,22 +242,6 @@ 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,6 +81,12 @@ 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] = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +196,6 @@ 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'),
|
||||
|
||||
@@ -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 status is not archived and is deployable
|
||||
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
|
||||
&& ($this->assetstatus->deployable == '1'))
|
||||
// The asset is not archived and the status is deployable
|
||||
if (($this->assetstatus) && ($this->archived == '0')
|
||||
&& ($this->assetstatus->status_type == 'deployable'))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -368,7 +368,7 @@ class Asset extends Depreciable
|
||||
if ($this->save()) {
|
||||
if (is_int($admin)) {
|
||||
$checkedOutBy = User::findOrFail($admin);
|
||||
} elseif (get_class($admin) === \App\Models\User::class) {
|
||||
} elseif ($admin && get_class($admin) === \App\Models\User::class) {
|
||||
$checkedOutBy = $admin;
|
||||
} else {
|
||||
$checkedOutBy = auth()->user();
|
||||
@@ -1146,9 +1146,7 @@ class Asset extends Depreciable
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->whereHas('assetstatus', function ($query) {
|
||||
$query->where('deployable', '=', 0)
|
||||
->where('pending', '=', 1)
|
||||
->where('archived', '=', 0);
|
||||
$query->where('status_type', '=', 'deployable');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1195,9 +1193,7 @@ class Asset extends Depreciable
|
||||
{
|
||||
return $query->whereNull('assets.assigned_to')
|
||||
->whereHas('assetstatus', function ($query) {
|
||||
$query->where('deployable', '=', 1)
|
||||
->where('pending', '=', 0)
|
||||
->where('archived', '=', 0);
|
||||
$query->where('status_type', '=', 'deployable');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1212,9 +1208,7 @@ class Asset extends Depreciable
|
||||
public function scopeUndeployable($query)
|
||||
{
|
||||
return $query->whereHas('assetstatus', function ($query) {
|
||||
$query->where('deployable', '=', 0)
|
||||
->where('pending', '=', 0)
|
||||
->where('archived', '=', 0);
|
||||
$query->where('status_type', '!=', 'deployable');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1229,7 +1223,7 @@ class Asset extends Depreciable
|
||||
public function scopeNotArchived($query)
|
||||
{
|
||||
return $query->whereHas('assetstatus', function ($query) {
|
||||
$query->where('archived', '=', 0);
|
||||
$query->where('status_type', '!=', 'archived');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1406,9 +1400,7 @@ class Asset extends Depreciable
|
||||
public function scopeArchived($query)
|
||||
{
|
||||
return $query->whereHas('assetstatus', function ($query) {
|
||||
$query->where('deployable', '=', 0)
|
||||
->where('pending', '=', 0)
|
||||
->where('archived', '=', 1);
|
||||
$query->where('status_type', '=', 'archived');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1440,9 +1432,8 @@ class Asset extends Depreciable
|
||||
return Company::scopeCompanyables($query->where($table.'.requestable', '=', 1))
|
||||
->whereHas('assetstatus', function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$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
|
||||
$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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1705,7 +1696,7 @@ class Asset extends Depreciable
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* THIS CLUNKY BIT IS VERY IMPORTANT
|
||||
@@ -1726,7 +1717,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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'user_id')
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ final class Company extends SnipeModel
|
||||
if ($current_user->company_id != null) {
|
||||
return $current_user->company_id;
|
||||
} else {
|
||||
return static::getIdFromInput($unescaped_input);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ 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
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,7 @@ 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',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -60,6 +61,8 @@ class Component extends SnipeModel
|
||||
'company_id',
|
||||
'supplier_id',
|
||||
'location_id',
|
||||
'manufacturer_id',
|
||||
'model_number',
|
||||
'name',
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
@@ -77,7 +80,15 @@ class Component extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'serial', 'purchase_cost', 'purchase_date', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'order_number',
|
||||
'serial',
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
'notes',
|
||||
'model_number',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
@@ -89,6 +100,7 @@ class Component extends SnipeModel
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
|
||||
@@ -183,6 +195,19 @@ 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
|
||||
*
|
||||
@@ -311,6 +336,19 @@ 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);
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Import extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'header_row' => 'array',
|
||||
'first_row' => 'array',
|
||||
|
||||
@@ -42,7 +42,7 @@ class Location extends SnipeModel
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether the model should inject it's identifier to the unique
|
||||
* Whether the model should inject its identifier to the unique
|
||||
* validation rules before attempting validation. If this property
|
||||
* is not set in the model it will default to true.
|
||||
*
|
||||
@@ -138,9 +138,7 @@ class Location extends SnipeModel
|
||||
{
|
||||
return $this->hasMany(\App\Models\Asset::class, 'location_id')
|
||||
->whereHas('assetstatus', function ($query) {
|
||||
$query->where('status_labels.deployable', '=', 1)
|
||||
->orWhere('status_labels.pending', '=', 1)
|
||||
->orWhere('status_labels.archived', '=', 0);
|
||||
$query->whereNot('status_labels.status_type', '=', 'archived');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ trait Loggable
|
||||
*/
|
||||
public function logCheckin($target, $note, $action_date = null, $originalValues = [])
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$log = new Actionlog;
|
||||
|
||||
if($target != null){
|
||||
@@ -171,39 +170,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ 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 == '');
|
||||
}
|
||||
|
||||
@@ -106,6 +107,10 @@ 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()
|
||||
{
|
||||
|
||||
@@ -51,36 +51,7 @@ 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'
|
||||
];
|
||||
|
||||
|
||||
@@ -23,17 +23,16 @@ class Statuslabel extends SnipeModel
|
||||
protected $rules = [
|
||||
'name' => 'required|string|unique_undeleted',
|
||||
'notes' => 'string|nullable',
|
||||
'deployable' => 'required',
|
||||
'pending' => 'required',
|
||||
'archived' => 'required',
|
||||
'status_type' => 'required|in:deployable,pending,archived,undeployable1',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'archived',
|
||||
'deployable',
|
||||
'status_type',
|
||||
'name',
|
||||
'notes',
|
||||
'pending',
|
||||
'color',
|
||||
'show_in_nav',
|
||||
'default_label',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
@@ -76,54 +75,6 @@ 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
|
||||
@@ -132,40 +83,10 @@ class Statuslabel extends SnipeModel
|
||||
*/
|
||||
public function scopeUndeployable()
|
||||
{
|
||||
return $this->where('pending', '=', 0)
|
||||
->where('archived', '=', 0)
|
||||
->where('deployable', '=', 0);
|
||||
return $this->whereNot('status_type', '=', 'deployable');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,6 +38,7 @@ class CheckoutConsumableNotification extends Notification
|
||||
$this->note = $note;
|
||||
$this->target = $checkedOutTo;
|
||||
$this->acceptance = $acceptance;
|
||||
$this->qty = $consumable->checkout_qty;
|
||||
|
||||
$this->settings = Setting::getSettings();
|
||||
}
|
||||
@@ -173,7 +174,6 @@ class CheckoutConsumableNotification extends Notification
|
||||
*/
|
||||
public function toMail()
|
||||
{
|
||||
Log::debug($this->item->getImageUrl());
|
||||
$eula = $this->item->getEula();
|
||||
$req_accept = $this->item->requireAcceptance();
|
||||
|
||||
@@ -188,6 +188,7 @@ class CheckoutConsumableNotification extends Notification
|
||||
'eula' => $eula,
|
||||
'req_accept' => $req_accept,
|
||||
'accept_url' => $accept_url,
|
||||
'qty' => $this->qty,
|
||||
])
|
||||
->subject(trans('mail.Confirm_consumable_delivery'));
|
||||
}
|
||||
|
||||
@@ -116,12 +116,6 @@ 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,
|
||||
|
||||
@@ -479,19 +479,6 @@ 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
|
||||
|
||||
@@ -66,8 +66,20 @@ 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->getStatuslabelType();
|
||||
return $this->model->assetstatus->status_type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -124,8 +124,15 @@ 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,
|
||||
|
||||
@@ -30,9 +30,9 @@ class StatusLabelPresenter extends Presenter
|
||||
'visible' => true,
|
||||
'formatter' => 'statuslabelsAssetLinkFormatter',
|
||||
],[
|
||||
'field' => 'type',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'field' => 'status_type',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => false,
|
||||
'title' => trans('admin/statuslabels/table.status_type'),
|
||||
'visible' => true,
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
@@ -38,7 +39,7 @@ class ValidationServiceProvider extends ServiceProvider
|
||||
|
||||
$rules = ['alert_email.*'=>'email'];
|
||||
$messages = [
|
||||
'alert_email.*'=>trans('validation.email_array'),
|
||||
'alert_email.*' => trans('validation.custom.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->getStatuslabelType()!='deployable')) {
|
||||
if (($value) && ($label = Statuslabel::find($value)) && ($label->status_type!='deployable')) {
|
||||
$fail(trans('admin/hardware/form.asset_not_deployable'));
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
"ext-exif": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^7.0",
|
||||
"fakerphp/faker": "^1.16",
|
||||
"larastan/larastan": "^2.9",
|
||||
"mockery/mockery": "^1.4",
|
||||
|
||||
1455
composer.lock
generated
1455
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -280,7 +280,6 @@ 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,
|
||||
@@ -373,7 +372,7 @@ return [
|
||||
'Image' => Intervention\Image\ImageServiceProvider::class,
|
||||
'Carbon' => Carbon\Carbon::class,
|
||||
'Helper' => App\Helpers\Helper::class,
|
||||
// makes it much easier to use 'Helper::blah' in blades (which is where we usually use this)
|
||||
'StorageHelper' => App\Helpers\StorageHelper::class,
|
||||
'Icon' => App\Helpers\IconHelper::class,
|
||||
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v7.0.13',
|
||||
'full_app_version' => 'v7.0.13 - build 15514-gdc0949da7',
|
||||
'build_version' => '15514',
|
||||
'full_app_version' => 'v7.0.13 - build 15666-g03b01689b',
|
||||
'build_version' => '15666',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'gdc0949da7',
|
||||
'full_hash' => 'v7.0.13-265-gdc0949da7',
|
||||
'hash_version' => 'g03b01689b',
|
||||
'full_hash' => 'v7.0.13-144-g03b01689b',
|
||||
'branch' => 'develop',
|
||||
);
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Category;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
@@ -156,4 +155,19 @@ 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,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ class AssetFactory extends Factory
|
||||
'rtd_location_id' => Location::factory(),
|
||||
'serial' => $this->faker->uuid(),
|
||||
'status_id' => function () {
|
||||
return Statuslabel::where('name', 'Ready to Deploy')->first() ?? Statuslabel::factory()->rtd()->create(['name' => 'Ready to Deploy']);
|
||||
// $status = Statuslabel::factory()->create(); dd($status) ;
|
||||
return Statuslabel::where('status_type', 'deployable')->first() ?? Statuslabel::factory()->create(['name' => 'Ready to Deploy', 'status_type' => 'deployable'])->id;
|
||||
},
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'asset_tag' => $this->faker->unixTime('now'),
|
||||
|
||||
@@ -7,6 +7,7 @@ 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;
|
||||
@@ -30,6 +31,7 @@ class ComponentFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
|
||||
return [
|
||||
'name' => $this->faker->text(20),
|
||||
'category_id' => Category::factory(),
|
||||
@@ -42,12 +44,14 @@ 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()
|
||||
{
|
||||
return $this->state(function () {
|
||||
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
|
||||
return $this->state(function () use ($manufacturer) {
|
||||
return [
|
||||
'name' => 'Crucial 4GB DDR3L-1600 SODIMM',
|
||||
'category_id' => function () {
|
||||
@@ -55,6 +59,7 @@ class ComponentFactory extends Factory
|
||||
},
|
||||
'qty' => 10,
|
||||
'min_amt' => 2,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
'location_id' => Location::factory(),
|
||||
];
|
||||
});
|
||||
@@ -62,7 +67,8 @@ class ComponentFactory extends Factory
|
||||
|
||||
public function ramCrucial8()
|
||||
{
|
||||
return $this->state(function () {
|
||||
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
|
||||
return $this->state(function () use ($manufacturer) {
|
||||
return [
|
||||
'name' => 'Crucial 8GB DDR3L-1600 SODIMM Memory for Mac',
|
||||
'category_id' => function () {
|
||||
@@ -70,13 +76,15 @@ class ComponentFactory extends Factory
|
||||
},
|
||||
'qty' => 10,
|
||||
'min_amt' => 2,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function ssdCrucial120()
|
||||
{
|
||||
return $this->state(function () {
|
||||
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
|
||||
return $this->state(function () use ($manufacturer) {
|
||||
return [
|
||||
'name' => 'Crucial BX300 120GB SATA Internal SSD',
|
||||
'category_id' => function () {
|
||||
@@ -84,13 +92,15 @@ class ComponentFactory extends Factory
|
||||
},
|
||||
'qty' => 10,
|
||||
'min_amt' => 2,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function ssdCrucial240()
|
||||
{
|
||||
return $this->state(function () {
|
||||
$manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']);
|
||||
return $this->state(function () use ($manufacturer) {
|
||||
return [
|
||||
'name' => 'Crucial BX300 240GB SATA Internal SSD',
|
||||
'category_id' => function () {
|
||||
@@ -98,6 +108,7 @@ class ComponentFactory extends Factory
|
||||
},
|
||||
'qty' => 10,
|
||||
'min_amt' => 2,
|
||||
'manufacturer_id' => $manufacturer->id,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
146
database/factories/ImportFactory.php
Normal file
146
database/factories/ImportFactory.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,7 @@ class StatuslabelFactory extends Factory
|
||||
'updated_at' => $this->faker->dateTime(),
|
||||
'created_by' => User::factory()->superuser(),
|
||||
'deleted_at' => null,
|
||||
'deployable' => 0,
|
||||
'pending' => 0,
|
||||
'archived' => 0,
|
||||
'status_type' => 'deployable',
|
||||
'notes' => '',
|
||||
];
|
||||
}
|
||||
@@ -40,7 +38,7 @@ class StatuslabelFactory extends Factory
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'notes' => $this->faker->sentence(),
|
||||
'deployable' => 1,
|
||||
'status_type' => 'deployable',
|
||||
'default_label' => 1,
|
||||
];
|
||||
});
|
||||
@@ -56,7 +54,7 @@ class StatuslabelFactory extends Factory
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'notes' => $this->faker->sentence(),
|
||||
'pending' => 1,
|
||||
'status_type' => 'pending',
|
||||
'default_label' => 1,
|
||||
];
|
||||
});
|
||||
@@ -67,8 +65,8 @@ class StatuslabelFactory extends Factory
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'notes' => 'These assets are permanently undeployable',
|
||||
'archived' => 1,
|
||||
'default_label' => 0,
|
||||
'status_type' => 'archived',
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -79,6 +77,7 @@ class StatuslabelFactory extends Factory
|
||||
return [
|
||||
'name' => 'Out for Diagnostics',
|
||||
'default_label' => 0,
|
||||
'status_type' => 'pending',
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -89,6 +88,7 @@ class StatuslabelFactory extends Factory
|
||||
return [
|
||||
'name' => 'Out for Repair',
|
||||
'default_label' => 0,
|
||||
'status_type' => 'pending',
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -99,6 +99,7 @@ class StatuslabelFactory extends Factory
|
||||
return [
|
||||
'name' => 'Broken - Not Fixable',
|
||||
'default_label' => 0,
|
||||
'status_type' => 'archived',
|
||||
];
|
||||
});
|
||||
}
|
||||
@@ -109,6 +110,7 @@ class StatuslabelFactory extends Factory
|
||||
return [
|
||||
'name' => 'Lost/Stolen',
|
||||
'default_label' => 0,
|
||||
'status_type' => 'archived',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use \Auth;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
@@ -37,6 +37,7 @@ 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()) {
|
||||
|
||||
@@ -17,21 +17,27 @@ 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()->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]);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -942,20 +942,21 @@ 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;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout: fixed;
|
||||
}
|
||||
/**
|
||||
* NEW STRIPING
|
||||
@@ -965,20 +966,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;
|
||||
}
|
||||
.row-new-striped div[class^="col"]:first-child {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -574,20 +574,21 @@ 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;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout: fixed;
|
||||
}
|
||||
/**
|
||||
* NEW STRIPING
|
||||
@@ -597,20 +598,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;
|
||||
}
|
||||
.row-new-striped div[class^="col"]:first-child {
|
||||
font-weight: bold;
|
||||
|
||||
28
public/css/dist/all.css
vendored
28
public/css/dist/all.css
vendored
@@ -21914,20 +21914,21 @@ 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;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout: fixed;
|
||||
}
|
||||
/**
|
||||
* NEW STRIPING
|
||||
@@ -21937,20 +21938,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;
|
||||
}
|
||||
.row-new-striped div[class^="col"]:first-child {
|
||||
font-weight: bold;
|
||||
@@ -23389,20 +23395,21 @@ 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;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout: fixed;
|
||||
}
|
||||
/**
|
||||
* NEW STRIPING
|
||||
@@ -23412,20 +23419,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;
|
||||
}
|
||||
.row-new-striped div[class^="col"]:first-child {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -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=efd9f439cb0586512d03172bcd9a5752",
|
||||
"/css/build/app.css": "/css/build/app.css?id=2f45befb40b9d7f038eeae9569c33a5f",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=1c3ffc5fb379e21523f2a9b03f986edb",
|
||||
"/css/build/app.css": "/css/build/app.css?id=d04f32982fb319ac35a32d362089f18b",
|
||||
"/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=e9509d7591637153f667461642e47e30",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=9f69886d7a8e4c383cd09a48573922b7",
|
||||
"/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",
|
||||
|
||||
320
public/vendor/livewire/livewire.esm.js
vendored
320
public/vendor/livewire/livewire.esm.js
vendored
@@ -1432,10 +1432,10 @@ var require_module_cjs = __commonJS({
|
||||
});
|
||||
}
|
||||
function cleanupElement(el) {
|
||||
if (el._x_cleanups) {
|
||||
while (el._x_cleanups.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
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()();
|
||||
}
|
||||
var observer = new MutationObserver(onMutate);
|
||||
var currentlyObserving = false;
|
||||
@@ -1673,27 +1673,23 @@ 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, getUtilities());
|
||||
return callback(el, memoizedUtilities);
|
||||
},
|
||||
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);
|
||||
@@ -2067,8 +2063,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
function destroyTree(root, walker = walk) {
|
||||
walker(root, (el) => {
|
||||
cleanupAttributes(el);
|
||||
cleanupElement(el);
|
||||
cleanupAttributes(el);
|
||||
});
|
||||
}
|
||||
function warnAboutMissingPlugins() {
|
||||
@@ -2561,7 +2557,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
}
|
||||
function bindInputValue(el, value) {
|
||||
if (el.type === "radio") {
|
||||
if (isRadio(el)) {
|
||||
if (el.attributes.value === void 0) {
|
||||
el.value = value;
|
||||
}
|
||||
@@ -2572,7 +2568,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
el.checked = checkedAttrLooseCompare(el.value, value);
|
||||
}
|
||||
}
|
||||
} else if (el.type === "checkbox") {
|
||||
} else if (isCheckbox(el)) {
|
||||
if (Number.isInteger(value)) {
|
||||
el.value = value;
|
||||
} else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) {
|
||||
@@ -2648,34 +2644,37 @@ ${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) {
|
||||
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);
|
||||
return booleanAttributes.has(attrName);
|
||||
}
|
||||
function attributeShouldntBePreservedIfFalsy(name) {
|
||||
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
|
||||
@@ -2708,6 +2707,12 @@ ${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() {
|
||||
@@ -2776,10 +2781,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;
|
||||
@@ -2861,7 +2866,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
get raw() {
|
||||
return raw;
|
||||
},
|
||||
version: "3.14.1",
|
||||
version: "3.14.3",
|
||||
flushAndStopDeferringMutations,
|
||||
dontAutoEvaluateFunctions,
|
||||
disableEffectScheduling,
|
||||
@@ -3070,7 +3075,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
placeInDom(el._x_teleport, target2, modifiers);
|
||||
});
|
||||
};
|
||||
cleanup(() => clone2.remove());
|
||||
cleanup(() => mutateDom(() => {
|
||||
clone2.remove();
|
||||
destroyTree(clone2);
|
||||
}));
|
||||
});
|
||||
var teleportContainerDuringClone = document.createElement("div");
|
||||
function getTarget(expression) {
|
||||
@@ -3294,7 +3302,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
setValue(getInputValue(el, modifiers, e, getValue()));
|
||||
});
|
||||
if (modifiers.includes("fill")) {
|
||||
if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
|
||||
if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
|
||||
setValue(getInputValue(el, modifiers, { target: el }, getValue()));
|
||||
}
|
||||
}
|
||||
@@ -3334,7 +3342,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 (el.type === "checkbox") {
|
||||
else if (isCheckbox(el)) {
|
||||
if (Array.isArray(currentValue)) {
|
||||
let newValue = null;
|
||||
if (modifiers.includes("number")) {
|
||||
@@ -3365,7 +3373,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
} else {
|
||||
let newValue;
|
||||
if (el.type === "radio") {
|
||||
if (isRadio(el)) {
|
||||
if (event.target.checked) {
|
||||
newValue = event.target.value;
|
||||
} else {
|
||||
@@ -3558,7 +3566,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
el._x_lookup = {};
|
||||
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
|
||||
cleanup(() => {
|
||||
Object.values(el._x_lookup).forEach((el2) => el2.remove());
|
||||
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
|
||||
destroyTree(el2);
|
||||
el2.remove();
|
||||
}));
|
||||
delete el._x_prevKeys;
|
||||
delete el._x_lookup;
|
||||
});
|
||||
@@ -3627,11 +3638,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
for (let i = 0; i < removes.length; i++) {
|
||||
let key = removes[i];
|
||||
if (!!lookup[key]._x_effects) {
|
||||
lookup[key]._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
lookup[key].remove();
|
||||
lookup[key] = null;
|
||||
if (!(key in lookup))
|
||||
continue;
|
||||
mutateDom(() => {
|
||||
destroyTree(lookup[key]);
|
||||
lookup[key].remove();
|
||||
});
|
||||
delete lookup[key];
|
||||
}
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
@@ -3752,12 +3764,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
el._x_currentIfEl = clone2;
|
||||
el._x_undoIf = () => {
|
||||
walk(clone2, (node) => {
|
||||
if (!!node._x_effects) {
|
||||
node._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
mutateDom(() => {
|
||||
destroyTree(clone2);
|
||||
clone2.remove();
|
||||
});
|
||||
clone2.remove();
|
||||
delete el._x_currentIfEl;
|
||||
};
|
||||
return clone2;
|
||||
@@ -3812,9 +3822,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/collapse/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js
|
||||
var require_module_cjs2 = __commonJS({
|
||||
"../alpine/packages/collapse/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
@@ -3887,7 +3897,7 @@ var require_module_cjs2 = __commonJS({
|
||||
start: { height: current + "px" },
|
||||
end: { height: full + "px" }
|
||||
}, () => el._x_isShown = true, () => {
|
||||
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
|
||||
if (el.getBoundingClientRect().height == full) {
|
||||
el.style.overflow = null;
|
||||
}
|
||||
});
|
||||
@@ -3933,9 +3943,9 @@ var require_module_cjs2 = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/focus/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js
|
||||
var require_module_cjs3 = __commonJS({
|
||||
"../alpine/packages/focus/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/focus/dist/module.cjs.js"(exports, module) {
|
||||
var __create2 = Object.create;
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
@@ -4935,9 +4945,9 @@ var require_module_cjs3 = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/persist/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js
|
||||
var require_module_cjs4 = __commonJS({
|
||||
"../alpine/packages/persist/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
@@ -5024,9 +5034,9 @@ var require_module_cjs4 = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/intersect/dist/module.cjs.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js
|
||||
var require_module_cjs5 = __commonJS({
|
||||
"../alpine/packages/intersect/dist/module.cjs.js"(exports, module) {
|
||||
"../../../../usr/local/lib/node_modules/@alpinejs/intersect/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
@@ -5106,8 +5116,80 @@ var require_module_cjs5 = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/anchor/dist/module.cjs.js
|
||||
// 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({
|
||||
"../alpine/packages/anchor/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
@@ -6645,7 +6727,7 @@ var require_nprogress = __commonJS({
|
||||
});
|
||||
|
||||
// ../alpine/packages/morph/dist/module.cjs.js
|
||||
var require_module_cjs7 = __commonJS({
|
||||
var require_module_cjs8 = __commonJS({
|
||||
"../alpine/packages/morph/dist/module.cjs.js"(exports, module) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
@@ -7006,9 +7088,9 @@ var require_module_cjs7 = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// ../alpine/packages/mask/dist/module.cjs.js
|
||||
var require_module_cjs8 = __commonJS({
|
||||
"../alpine/packages/mask/dist/module.cjs.js"(exports, module) {
|
||||
// ../../../../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) {
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||
@@ -8509,7 +8591,8 @@ 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_anchor = __toESM(require_module_cjs6());
|
||||
var import_resize = __toESM(require_module_cjs6());
|
||||
var import_anchor = __toESM(require_module_cjs7());
|
||||
|
||||
// js/plugins/navigate/history.js
|
||||
var Snapshot = class {
|
||||
@@ -8660,7 +8743,7 @@ function extractDestinationFromLink(linkEl) {
|
||||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
@@ -8782,8 +8865,10 @@ function restoreScrollPositionOrScrollToTop() {
|
||||
}
|
||||
};
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8932,6 +9017,44 @@ 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 = [
|
||||
@@ -9070,7 +9193,7 @@ var autofocus = false;
|
||||
function navigate_default(Alpine19) {
|
||||
Alpine19.navigate = (url) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -9087,17 +9210,21 @@ 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 = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -9111,7 +9238,7 @@ function navigate_default(Alpine19) {
|
||||
function navigateTo(destination, shouldPushToHistoryState = true) {
|
||||
showProgressBar && showAndStartProgressBar();
|
||||
fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => {
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigating");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigating");
|
||||
restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway();
|
||||
showProgressBar && finishAndHideProgressBar();
|
||||
cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement();
|
||||
@@ -9119,6 +9246,7 @@ function navigate_default(Alpine19) {
|
||||
preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => {
|
||||
enablePersist && storePersistantElementsForLater((persistedEl) => {
|
||||
packUpPersistedTeleports(persistedEl);
|
||||
packUpPersistedPopovers(persistedEl);
|
||||
});
|
||||
if (shouldPushToHistoryState) {
|
||||
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
|
||||
@@ -9129,6 +9257,7 @@ function navigate_default(Alpine19) {
|
||||
removeAnyLeftOverStaleTeleportTargets(document.body);
|
||||
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
|
||||
unPackPersistedTeleports(persistedEl);
|
||||
unPackPersistedPopovers(persistedEl);
|
||||
});
|
||||
restoreScrollPositionOrScrollToTop();
|
||||
afterNewScriptsAreDoneLoading(() => {
|
||||
@@ -9137,7 +9266,7 @@ function navigate_default(Alpine19) {
|
||||
autofocus && autofocusElementsWithTheAutofocusAttribute();
|
||||
});
|
||||
nowInitializeAlpineOnTheNewPage(Alpine19);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9147,7 +9276,7 @@ function navigate_default(Alpine19) {
|
||||
whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => {
|
||||
ifThePageBeingVisitedHasntBeenCached((url) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: true,
|
||||
cached: false
|
||||
@@ -9159,7 +9288,7 @@ function navigate_default(Alpine19) {
|
||||
});
|
||||
}, (html, url, currentPageUrl, currentPageKey) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: true,
|
||||
cached: true
|
||||
@@ -9167,29 +9296,31 @@ function navigate_default(Alpine19) {
|
||||
if (prevented)
|
||||
return;
|
||||
storeScrollInformationInHtmlBeforeNavigatingAway();
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigating");
|
||||
fireEventForOtherLibrariesToHookInto("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);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
setTimeout(() => {
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
}
|
||||
function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) {
|
||||
@@ -9206,7 +9337,7 @@ function preventAlpineFromPickingUpDomChanges(Alpine19, callback) {
|
||||
});
|
||||
});
|
||||
}
|
||||
function fireEventForOtherLibariesToHookInto(name, detail) {
|
||||
function fireEventForOtherLibrariesToHookInto(name, detail) {
|
||||
let event = new CustomEvent(name, {
|
||||
cancelable: true,
|
||||
bubbles: true,
|
||||
@@ -9441,8 +9572,8 @@ function fromQueryString(search) {
|
||||
}
|
||||
|
||||
// js/lifecycle.js
|
||||
var import_morph = __toESM(require_module_cjs7());
|
||||
var import_mask = __toESM(require_module_cjs8());
|
||||
var import_morph = __toESM(require_module_cjs8());
|
||||
var import_mask = __toESM(require_module_cjs9());
|
||||
var import_alpinejs5 = __toESM(require_module_cjs());
|
||||
function start() {
|
||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||
@@ -9451,6 +9582,7 @@ 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);
|
||||
@@ -9721,6 +9853,7 @@ function morph2(component, el, html) {
|
||||
},
|
||||
lookahead: false
|
||||
});
|
||||
trigger("morphed", { el, component });
|
||||
}
|
||||
function isntElement(el) {
|
||||
return typeof el.hasAttribute !== "function";
|
||||
@@ -10790,3 +10923,4 @@ focus-trap/dist/focus-trap.js:
|
||||
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
||||
*)
|
||||
*/
|
||||
//# sourceMappingURL=livewire.esm.js.map
|
||||
|
||||
290
public/vendor/livewire/livewire.js
vendored
290
public/vendor/livewire/livewire.js
vendored
@@ -851,10 +851,9 @@
|
||||
});
|
||||
}
|
||||
function cleanupElement(el) {
|
||||
if (el._x_cleanups) {
|
||||
while (el._x_cleanups.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
el._x_effects?.forEach(dequeueJob);
|
||||
while (el._x_cleanups?.length)
|
||||
el._x_cleanups.pop()();
|
||||
}
|
||||
var observer = new MutationObserver(onMutate);
|
||||
var currentlyObserving = false;
|
||||
@@ -1092,27 +1091,23 @@
|
||||
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, getUtilities());
|
||||
return callback(el, memoizedUtilities);
|
||||
},
|
||||
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);
|
||||
@@ -1486,8 +1481,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
function destroyTree(root, walker = walk) {
|
||||
walker(root, (el) => {
|
||||
cleanupAttributes(el);
|
||||
cleanupElement(el);
|
||||
cleanupAttributes(el);
|
||||
});
|
||||
}
|
||||
function warnAboutMissingPlugins() {
|
||||
@@ -1980,7 +1975,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
}
|
||||
function bindInputValue(el, value) {
|
||||
if (el.type === "radio") {
|
||||
if (isRadio(el)) {
|
||||
if (el.attributes.value === void 0) {
|
||||
el.value = value;
|
||||
}
|
||||
@@ -1991,7 +1986,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
el.checked = checkedAttrLooseCompare(el.value, value);
|
||||
}
|
||||
}
|
||||
} else if (el.type === "checkbox") {
|
||||
} else if (isCheckbox(el)) {
|
||||
if (Number.isInteger(value)) {
|
||||
el.value = value;
|
||||
} else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) {
|
||||
@@ -2067,34 +2062,37 @@ ${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) {
|
||||
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);
|
||||
return booleanAttributes.has(attrName);
|
||||
}
|
||||
function attributeShouldntBePreservedIfFalsy(name) {
|
||||
return !["aria-pressed", "aria-checked", "aria-expanded", "aria-selected"].includes(name);
|
||||
@@ -2127,6 +2125,12 @@ ${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() {
|
||||
@@ -2195,10 +2199,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;
|
||||
@@ -2280,7 +2284,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
get raw() {
|
||||
return raw;
|
||||
},
|
||||
version: "3.14.1",
|
||||
version: "3.14.3",
|
||||
flushAndStopDeferringMutations,
|
||||
dontAutoEvaluateFunctions,
|
||||
disableEffectScheduling,
|
||||
@@ -3136,7 +3140,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
placeInDom(el._x_teleport, target2, modifiers);
|
||||
});
|
||||
};
|
||||
cleanup2(() => clone2.remove());
|
||||
cleanup2(() => mutateDom(() => {
|
||||
clone2.remove();
|
||||
destroyTree(clone2);
|
||||
}));
|
||||
});
|
||||
var teleportContainerDuringClone = document.createElement("div");
|
||||
function getTarget(expression) {
|
||||
@@ -3360,7 +3367,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
setValue(getInputValue(el, modifiers, e, getValue()));
|
||||
});
|
||||
if (modifiers.includes("fill")) {
|
||||
if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
|
||||
if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) {
|
||||
setValue(getInputValue(el, modifiers, { target: el }, getValue()));
|
||||
}
|
||||
}
|
||||
@@ -3400,7 +3407,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 (el.type === "checkbox") {
|
||||
else if (isCheckbox(el)) {
|
||||
if (Array.isArray(currentValue)) {
|
||||
let newValue = null;
|
||||
if (modifiers.includes("number")) {
|
||||
@@ -3431,7 +3438,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
} else {
|
||||
let newValue;
|
||||
if (el.type === "radio") {
|
||||
if (isRadio(el)) {
|
||||
if (event.target.checked) {
|
||||
newValue = event.target.value;
|
||||
} else {
|
||||
@@ -3624,7 +3631,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
el._x_lookup = {};
|
||||
effect3(() => loop(el, iteratorNames, evaluateItems, evaluateKey));
|
||||
cleanup2(() => {
|
||||
Object.values(el._x_lookup).forEach((el2) => el2.remove());
|
||||
Object.values(el._x_lookup).forEach((el2) => mutateDom(() => {
|
||||
destroyTree(el2);
|
||||
el2.remove();
|
||||
}));
|
||||
delete el._x_prevKeys;
|
||||
delete el._x_lookup;
|
||||
});
|
||||
@@ -3693,11 +3703,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
for (let i = 0; i < removes.length; i++) {
|
||||
let key = removes[i];
|
||||
if (!!lookup[key]._x_effects) {
|
||||
lookup[key]._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
lookup[key].remove();
|
||||
lookup[key] = null;
|
||||
if (!(key in lookup))
|
||||
continue;
|
||||
mutateDom(() => {
|
||||
destroyTree(lookup[key]);
|
||||
lookup[key].remove();
|
||||
});
|
||||
delete lookup[key];
|
||||
}
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
@@ -3818,12 +3829,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
el._x_currentIfEl = clone2;
|
||||
el._x_undoIf = () => {
|
||||
walk(clone2, (node) => {
|
||||
if (!!node._x_effects) {
|
||||
node._x_effects.forEach(dequeueJob);
|
||||
}
|
||||
mutateDom(() => {
|
||||
destroyTree(clone2);
|
||||
clone2.remove();
|
||||
});
|
||||
clone2.remove();
|
||||
delete el._x_currentIfEl;
|
||||
};
|
||||
return clone2;
|
||||
@@ -4762,7 +4771,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
};
|
||||
|
||||
// ../alpine/packages/collapse/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/collapse/dist/module.esm.js
|
||||
function src_default2(Alpine3) {
|
||||
Alpine3.directive("collapse", collapse);
|
||||
collapse.inline = (el, { modifiers }) => {
|
||||
@@ -4812,7 +4821,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
start: { height: current + "px" },
|
||||
end: { height: full + "px" }
|
||||
}, () => el._x_isShown = true, () => {
|
||||
if (Math.abs(el.getBoundingClientRect().height - full) < 1) {
|
||||
if (el.getBoundingClientRect().height == full) {
|
||||
el.style.overflow = null;
|
||||
}
|
||||
});
|
||||
@@ -4856,7 +4865,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default2 = src_default2;
|
||||
|
||||
// ../alpine/packages/focus/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/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";
|
||||
@@ -4968,11 +4977,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
var checked = getCheckedRadio(radioSet, node.form);
|
||||
return !checked || checked === node;
|
||||
};
|
||||
var isRadio = function isRadio2(node) {
|
||||
var isRadio2 = function isRadio22(node) {
|
||||
return isInput(node) && node.type === "radio";
|
||||
};
|
||||
var isNonTabbableRadio = function isNonTabbableRadio2(node) {
|
||||
return isRadio(node) && !isTabbableRadio(node);
|
||||
return isRadio2(node) && !isTabbableRadio(node);
|
||||
};
|
||||
var isZeroArea = function isZeroArea2(node) {
|
||||
var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;
|
||||
@@ -5805,7 +5814,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default3 = src_default3;
|
||||
|
||||
// ../alpine/packages/persist/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/persist/dist/module.esm.js
|
||||
function src_default4(Alpine3) {
|
||||
let persist = () => {
|
||||
let alias;
|
||||
@@ -5867,7 +5876,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
var module_default4 = src_default4;
|
||||
|
||||
// ../alpine/packages/intersect/dist/module.esm.js
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/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);
|
||||
@@ -5922,6 +5931,51 @@ ${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;
|
||||
@@ -7096,7 +7150,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
platform: platformWithCache
|
||||
});
|
||||
};
|
||||
function src_default6(Alpine3) {
|
||||
function src_default7(Alpine3) {
|
||||
Alpine3.magic("anchor", (el) => {
|
||||
if (!el._x_anchor)
|
||||
throw "Alpine: No x-anchor directive found on element using $anchor...";
|
||||
@@ -7154,7 +7208,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let unstyled = modifiers.includes("no-style");
|
||||
return { placement, offsetValue, unstyled };
|
||||
}
|
||||
var module_default6 = src_default6;
|
||||
var module_default7 = src_default7;
|
||||
|
||||
// js/plugins/navigate/history.js
|
||||
var Snapshot = class {
|
||||
@@ -7305,7 +7359,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
@@ -7426,8 +7480,10 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
}
|
||||
};
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
queueMicrotask(() => {
|
||||
scroll(document.body);
|
||||
document.querySelectorAll(["[x-navigate\\:scroll]", "[wire\\:scroll]"]).forEach(scroll);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7575,6 +7631,44 @@ ${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 = [
|
||||
@@ -7713,7 +7807,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
function navigate_default(Alpine3) {
|
||||
Alpine3.navigate = (url) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -7730,17 +7824,21 @@ ${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 = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -7754,7 +7852,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
function navigateTo(destination, shouldPushToHistoryState = true) {
|
||||
showProgressBar && showAndStartProgressBar();
|
||||
fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => {
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigating");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigating");
|
||||
restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway();
|
||||
showProgressBar && finishAndHideProgressBar();
|
||||
cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement();
|
||||
@@ -7762,6 +7860,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => {
|
||||
enablePersist && storePersistantElementsForLater((persistedEl) => {
|
||||
packUpPersistedTeleports(persistedEl);
|
||||
packUpPersistedPopovers(persistedEl);
|
||||
});
|
||||
if (shouldPushToHistoryState) {
|
||||
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
|
||||
@@ -7772,6 +7871,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
removeAnyLeftOverStaleTeleportTargets(document.body);
|
||||
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
|
||||
unPackPersistedTeleports(persistedEl);
|
||||
unPackPersistedPopovers(persistedEl);
|
||||
});
|
||||
restoreScrollPositionOrScrollToTop();
|
||||
afterNewScriptsAreDoneLoading(() => {
|
||||
@@ -7780,7 +7880,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
autofocus && autofocusElementsWithTheAutofocusAttribute();
|
||||
});
|
||||
nowInitializeAlpineOnTheNewPage(Alpine3);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7790,7 +7890,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => {
|
||||
ifThePageBeingVisitedHasntBeenCached((url) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: true,
|
||||
cached: false
|
||||
@@ -7802,7 +7902,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
}, (html, url, currentPageUrl, currentPageKey) => {
|
||||
let destination = createUrlObjectFromString(url);
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: true,
|
||||
cached: true
|
||||
@@ -7810,29 +7910,31 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
if (prevented)
|
||||
return;
|
||||
storeScrollInformationInHtmlBeforeNavigatingAway();
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigating");
|
||||
fireEventForOtherLibrariesToHookInto("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);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
setTimeout(() => {
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
}
|
||||
function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) {
|
||||
@@ -7849,7 +7951,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
});
|
||||
}
|
||||
function fireEventForOtherLibariesToHookInto(name, detail) {
|
||||
function fireEventForOtherLibrariesToHookInto(name, detail) {
|
||||
let event = new CustomEvent(name, {
|
||||
cancelable: true,
|
||||
bubbles: true,
|
||||
@@ -8413,13 +8515,13 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
to.setAttribute("id", fromId);
|
||||
to.id = fromId;
|
||||
}
|
||||
function src_default7(Alpine3) {
|
||||
function src_default8(Alpine3) {
|
||||
Alpine3.morph = morph;
|
||||
}
|
||||
var module_default7 = src_default7;
|
||||
var module_default8 = src_default8;
|
||||
|
||||
// ../alpine/packages/mask/dist/module.esm.js
|
||||
function src_default8(Alpine3) {
|
||||
// ../../../../usr/local/lib/node_modules/@alpinejs/mask/dist/module.esm.js
|
||||
function src_default9(Alpine3) {
|
||||
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||
let templateFn = () => expression;
|
||||
let lastInputValue = "";
|
||||
@@ -8581,22 +8683,23 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
});
|
||||
return template;
|
||||
}
|
||||
var module_default8 = src_default8;
|
||||
var module_default9 = src_default9;
|
||||
|
||||
// js/lifecycle.js
|
||||
function start2() {
|
||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||
dispatch(document, "livewire:init");
|
||||
dispatch(document, "livewire:initializing");
|
||||
module_default.plugin(module_default7);
|
||||
module_default.plugin(module_default8);
|
||||
module_default.plugin(history2);
|
||||
module_default.plugin(module_default5);
|
||||
module_default.plugin(module_default2);
|
||||
module_default.plugin(module_default6);
|
||||
module_default.plugin(module_default2);
|
||||
module_default.plugin(module_default7);
|
||||
module_default.plugin(module_default3);
|
||||
module_default.plugin(module_default4);
|
||||
module_default.plugin(navigate_default);
|
||||
module_default.plugin(module_default8);
|
||||
module_default.plugin(module_default9);
|
||||
module_default.addRootSelector(() => "[wire\\:id]");
|
||||
module_default.onAttributesAdded((el, attributes) => {
|
||||
if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
|
||||
@@ -8855,6 +8958,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
},
|
||||
lookahead: false
|
||||
});
|
||||
trigger2("morphed", { el, component });
|
||||
}
|
||||
function isntElement(el) {
|
||||
return typeof el.hasAttribute !== "function";
|
||||
|
||||
12
public/vendor/livewire/livewire.min.js
vendored
12
public/vendor/livewire/livewire.min.js
vendored
File diff suppressed because one or more lines are too long
6
public/vendor/livewire/livewire.min.js.map
vendored
6
public/vendor/livewire/livewire.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
@@ -1,2 +1,2 @@
|
||||
|
||||
{"/livewire.js":"87e1046f"}
|
||||
{"/livewire.js":"38dc8241"}
|
||||
|
||||
@@ -621,31 +621,30 @@ 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;
|
||||
line-height: 2.6;
|
||||
padding: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 3px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
word-wrap: break-word;
|
||||
table-layout:fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -656,25 +655,28 @@ 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,6 +14,8 @@ 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' => [
|
||||
@@ -79,6 +81,11 @@ 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,5 +385,6 @@ 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.',
|
||||
|
||||
];
|
||||
|
||||
@@ -230,7 +230,7 @@ return [
|
||||
'purchase_date' => 'Purchase Date',
|
||||
'qty' => 'QTY',
|
||||
'quantity' => 'Quantity',
|
||||
'quantity_minimum' => 'You have :count items below or almost below minimum quantity levels',
|
||||
'quantity_minimum' => 'You have one item below or almost below minimum quantity levels|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,6 +434,7 @@ 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',
|
||||
|
||||
@@ -173,6 +173,7 @@ return [
|
||||
'ulid' => 'The :attribute field must be a valid ULID.',
|
||||
'uuid' => 'The :attribute field must be a valid UUID.',
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Language Lines
|
||||
@@ -194,7 +195,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 is reflects php Y-m-d, which non-PHP
|
||||
// We use this because the default error message for date_format 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',
|
||||
@@ -206,6 +207,13 @@ 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.'],
|
||||
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -155,102 +155,16 @@
|
||||
@can('accessories.files', $accessory)
|
||||
<div class="tab-pane" id="files">
|
||||
|
||||
<div class="table table-responsive">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table
|
||||
data-cookie-id-table="accessoryUploadsTable"
|
||||
data-id-table="accessoryUploadsTable"
|
||||
id="accessoryUploadsTable"
|
||||
data-search="true"
|
||||
data-pagination="true"
|
||||
data-side-pagination="client"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-show-footer="true"
|
||||
data-toolbar="#upload-toolbar"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
data-sort-name="name"
|
||||
class="table table-striped snipe-table"
|
||||
data-export-options='{
|
||||
"fileName": "export-accessories-uploads-{{ str_slug($accessory->name) }}-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","delete","download","icon"]
|
||||
}'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-visible="true" data-field="icon" data-sortable="true">{{trans('general.file_type')}}</th>
|
||||
<th class="col-md-2" data-searchable="true" data-visible="true" data-field="image">{{ trans('general.image') }}</th>
|
||||
<th class="col-md-2" data-searchable="true" data-visible="true" data-field="filename" data-sortable="true">{{ trans('general.file_name') }}</th>
|
||||
<th class="col-md-1" data-searchable="true" data-visible="true" data-field="filesize">{{ trans('general.filesize') }}</th>
|
||||
<th class="col-md-2" data-searchable="true" data-visible="true" data-field="notes" data-sortable="true">{{ trans('general.notes') }}</th>
|
||||
<th class="col-md-1" data-searchable="true" data-visible="true" data-field="download">{{ trans('general.download') }}</th>
|
||||
<th class="col-md-2" data-searchable="true" data-visible="true" data-field="created_at" data-sortable="true">{{ trans('general.created_at') }}</th>
|
||||
<th class="col-md-1" data-searchable="true" data-visible="true" data-field="actions">{{ trans('table.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if ($accessory->uploads->count() > 0)
|
||||
@foreach ($accessory->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
<x-icon type="paperclip" class="fa-2x" />
|
||||
<i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ Helper::filetype_icon($file->filename) }}</span>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
@if ( Helper::checkUploadIsImage($file->get_src('accessories')))
|
||||
<a href="{{ route('show.accessoryfile', ['accessoryId' => $accessory->id, 'fileId' => $file->id, 'download' => 'false']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show.accessoryfile', ['accessoryId' => $accessory->id, 'fileId' => $file->id]) }}" class="img-thumbnail" style="max-width: 50px;"></a>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->filename }}
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/accessories/'.$file->filename) ? Storage::size('private_uploads/accessories/'.$file->filename) : '') }}">
|
||||
{{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/accessories/'.$file->filename) ? Storage::size('private_uploads/accessories/'.$file->filename) : '') }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td style="white-space: nowrap;">
|
||||
@if ($file->filename)
|
||||
<a href="{{ route('show.accessoryfile', [$accessory->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show.accessoryfile', [$accessory->id, $file->id, 'inline' => 'true']) }}" class="btn btn-sm btn-default" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $file->created_at }}</td>
|
||||
<td>
|
||||
<a class="btn delete-asset btn-danger btn-sm" href="{{ route('delete/accessoryfile', [$accessory->id, $file->id]) }}" data-content="{{ trans('general.delete_confirm', ['item' => $file->filename]) }}" data-title="{{ trans('general.delete') }}">
|
||||
<i class="fas fa-trash icon-white" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.delete') }}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@else
|
||||
<tr>
|
||||
<td colspan="8">{{ trans('general.no_results') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<x-filestable
|
||||
filepath="private_uploads/accessories/"
|
||||
showfile_routename="show.accessoryfile"
|
||||
deletefile_routename="delete/accessoryfile"
|
||||
:object="$accessory" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.tab-pane -->
|
||||
</div> <!-- /.tab-pane -->
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@@ -265,7 +179,8 @@
|
||||
@if ($accessory->image!='')
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 15px;">
|
||||
<a href="{{ Storage::disk('public')->url('accessories/'.e($accessory->image)) }}" data-toggle="lightbox"><img src="{{ Storage::disk('public')->url('accessories/'.e($accessory->image)) }}" class="img-responsive img-thumbnail" alt="{{ $accessory->name }}"></a>
|
||||
<a href="{{ Storage::disk('public')->url('accessories/'.e($accessory->image)) }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ Storage::disk('public')->url('accessories/'.e($accessory->image)) }}" class="img-responsive img-thumbnail" alt="{{ $accessory->name }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -301,7 +216,7 @@
|
||||
{{ trans('general.notes') }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="col-md-9" style="word-wrap: break-word;">
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($accessory->notes)) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,20 +103,23 @@
|
||||
</div>
|
||||
@can('self.profile')
|
||||
<div class="col-md-12">
|
||||
<a href="{{ route('profile') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">
|
||||
<a href="{{ route('profile') }}" style="width: 100%;" class="btn btn-sm btn-warning btn-social btn-block hidden-print">
|
||||
<x-icon type="edit" />
|
||||
{{ trans('general.editprofile') }}
|
||||
</a>
|
||||
</div>
|
||||
@endcan
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('account.password.index') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('account.password.index') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="password" class="fa-fw" />
|
||||
{{ trans('general.changepassword') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@can('self.api')
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('user.api') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('user.api') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="api-key" class="fa-fw" />
|
||||
{{ trans('general.manage_api_keys') }}
|
||||
</a>
|
||||
</div>
|
||||
@@ -124,7 +127,8 @@
|
||||
|
||||
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('profile.print') }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print" target="_blank" rel="noopener">
|
||||
<a href="{{ route('profile.print') }}" style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" target="_blank" rel="noopener">
|
||||
<x-icon type="print" class="fa-fw" />
|
||||
{{ trans('admin/users/general.print_assigned') }}
|
||||
</a>
|
||||
</div>
|
||||
@@ -134,10 +138,16 @@
|
||||
@if (!empty($user->email))
|
||||
<form action="{{ route('profile.email_assets') }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary hidden-print" rel="noopener">{{ trans('admin/users/general.email_assigned') }}</button>
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print" rel="noopener">
|
||||
<x-icon type="email" class="fa-fw" />
|
||||
{{ trans('admin/users/general.email_assigned') }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary hidden-print" rel="noopener" disabled title="{{ trans('admin/users/message.user_has_no_email') }}">{{ trans('admin/users/general.email_assigned') }}</button>
|
||||
<button style="width: 100%;" class="btn btn-sm btn-primary btn-social btn-block hidden-print disabled" rel="noopener" disabled title="{{ trans('admin/users/message.user_has_no_email') }}">
|
||||
<x-icon type="email" class="fa-fw" />
|
||||
{{ trans('admin/users/general.email_assigned') }}
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user