Compare commits
93 Commits
feature/sc
...
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 | ||
|
|
524a442724 | ||
|
|
3153bbb13f | ||
|
|
3f74ff25d2 | ||
|
|
b6340532d7 | ||
|
|
c71411465a |
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
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,11 +26,19 @@ class StoreAssetRequest extends ImageUploadRequest
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
// Guard against users passing in an array for company_id instead of an integer.
|
||||
// If the company_id is not an integer then we simply use what was
|
||||
// provided to be caught by model level validation later.
|
||||
// The use of is_numeric accounts for 1 and '1'.
|
||||
$idForCurrentUser = is_numeric($this->company_id)
|
||||
? Company::getIdForCurrentUser($this->company_id)
|
||||
: $this->company_id;
|
||||
|
||||
$this->parseLastAuditDate();
|
||||
|
||||
$this->merge([
|
||||
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
|
||||
'company_id' => Company::getIdForCurrentUser($this->company_id),
|
||||
'company_id' => $idForCurrentUser,
|
||||
'assigned_to' => $assigned_to ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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)) ? [
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -23,9 +23,9 @@ class StatuslabelsTransformer
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $statuslabel->id,
|
||||
'reorder_icon' => '<i class="fas fa-ellipsis-v"></i>',
|
||||
'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,17 +148,23 @@ 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 ----------------');
|
||||
@@ -237,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'),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -21,14 +21,6 @@ class StatusLabelPresenter extends Presenter
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
],
|
||||
[
|
||||
'field' => 'reorder_icon',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => false,
|
||||
'title' => trans('admin/custom_fields/general.reorder'),
|
||||
'visible' => true,
|
||||
], [
|
||||
'field' => 'name',
|
||||
'searchable' => true,
|
||||
@@ -38,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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
170
composer.lock
generated
170
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "3819ab4ef72eb77fabe494c0e746b83b",
|
||||
"content-hash": "5341bc5be02b3c33e28e46e06dd99f29",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
@@ -4461,16 +4461,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v3.5.9",
|
||||
"version": "v3.5.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "d04a229058afa76116d0e39209943a8ea3a7f888"
|
||||
"reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/d04a229058afa76116d0e39209943a8ea3a7f888",
|
||||
"reference": "d04a229058afa76116d0e39209943a8ea3a7f888",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d",
|
||||
"reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4525,7 +4525,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.5.9"
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.5.12"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4533,7 +4533,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-01T12:40:06+00:00"
|
||||
"time": "2024-10-15T19:35:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
@@ -11790,101 +11790,6 @@
|
||||
],
|
||||
"time": "2024-04-13T18:00:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
"version": "v7.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paratestphp/paratest.git",
|
||||
"reference": "551f46f52a93177d873f3be08a1649ae886b4a30"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30",
|
||||
"reference": "551f46f52a93177d873f3be08a1649ae886b4a30",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"fidry/cpu-core-counter": "^0.5.1 || ^1.0.0",
|
||||
"jean85/pretty-package-versions": "^2.0.5",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
|
||||
"phpunit/php-code-coverage": "^10.1.7",
|
||||
"phpunit/php-file-iterator": "^4.1.0",
|
||||
"phpunit/php-timer": "^6.0",
|
||||
"phpunit/phpunit": "^10.4.2",
|
||||
"sebastian/environment": "^6.0.1",
|
||||
"symfony/console": "^6.3.4 || ^7.0.0",
|
||||
"symfony/process": "^6.3.4 || ^7.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^12.0.0",
|
||||
"ext-pcov": "*",
|
||||
"ext-posix": "*",
|
||||
"infection/infection": "^0.27.6",
|
||||
"phpstan/phpstan": "^1.10.40",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1.4",
|
||||
"phpstan/phpstan-phpunit": "^1.3.15",
|
||||
"phpstan/phpstan-strict-rules": "^1.5.2",
|
||||
"squizlabs/php_codesniffer": "^3.7.2",
|
||||
"symfony/filesystem": "^6.3.1 || ^7.0.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/paratest",
|
||||
"bin/paratest.bat",
|
||||
"bin/paratest_for_phpstorm"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ParaTest\\": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Brian Scaturro",
|
||||
"email": "scaturrob@gmail.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Filippo Tessarotto",
|
||||
"email": "zoeslam@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Parallel testing for PHP",
|
||||
"homepage": "https://github.com/paratestphp/paratest",
|
||||
"keywords": [
|
||||
"concurrent",
|
||||
"parallel",
|
||||
"phpunit",
|
||||
"testing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paratestphp/paratest/issues",
|
||||
"source": "https://github.com/paratestphp/paratest/tree/v7.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/Slamdunk",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://paypal.me/filippotessarotto",
|
||||
"type": "paypal"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-31T09:24:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "clue/ndjson-react",
|
||||
"version": "v1.3.0",
|
||||
@@ -12781,65 +12686,6 @@
|
||||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
|
||||
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.0.0",
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.4",
|
||||
"vimeo/psalm": "^4.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jean85\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alessandro Lai",
|
||||
"email": "alessandro.lai85@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to get pretty versions strings of installed dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"package",
|
||||
"release",
|
||||
"versions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
|
||||
},
|
||||
"time": "2024-03-08T09:58:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.3.0",
|
||||
@@ -16658,5 +16504,5 @@
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
@@ -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,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('status_labels', function (Blueprint $table) {
|
||||
$table->tinyInteger('display_order')->nullable()->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('status_labels', function (Blueprint $table) {
|
||||
$table->dropColumn('display_order');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -35,7 +35,6 @@
|
||||
"select2": "4.0.13",
|
||||
"sheetjs": "^2.0.0",
|
||||
"signature_pad": "^4.2.0",
|
||||
"tablednd": "^1.0.5",
|
||||
"tableexport.jquery.plugin": "1.30.0",
|
||||
"tether": "^1.4.0",
|
||||
"webpack": "^5.94.0"
|
||||
@@ -10389,11 +10388,6 @@
|
||||
"acorn-node": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tablednd": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tablednd/-/tablednd-1.0.5.tgz",
|
||||
"integrity": "sha512-iC230jusaekjzzAy0jigZLbzb7jKKnW9xiNiZ4W17p7y8AvBYf5KunyrIE3HwL1FhL2VEeogZtjP5VeBeIW4xg=="
|
||||
},
|
||||
"node_modules/tableexport.jquery.plugin": {
|
||||
"version": "1.30.0",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
"select2": "4.0.13",
|
||||
"sheetjs": "^2.0.0",
|
||||
"signature_pad": "^4.2.0",
|
||||
"tablednd": "^1.0.5",
|
||||
"tableexport.jquery.plugin": "1.30.0",
|
||||
"tether": "^1.4.0",
|
||||
"webpack": "^5.94.0"
|
||||
|
||||
2
public/css/dist/bootstrap-table.css
vendored
2
public/css/dist/bootstrap-table.css
vendored
@@ -2,8 +2,6 @@
|
||||
|
||||
.fix-sticky{position:fixed !important;overflow:hidden;z-index:100}.fix-sticky table thead{background:#fff}.fix-sticky table thead.thead-light{background:#e9ecef}.fix-sticky table thead.thead-dark{background:#212529}
|
||||
|
||||
.reorder-rows-on-drag-class td{background-color:#eee;box-shadow:6px 4px 5px 1px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-box-shadow:6px 4px 5px 1px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset}.reorder-rows-on-drag-class td:last-child{box-shadow:0 9px 4px -4px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset,-1px 0 0 #ccc inset;-box-shadow:0 9px 4px -4px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset,-1px 0 0 #ccc inset}
|
||||
|
||||
/*
|
||||
* dragtable
|
||||
*
|
||||
|
||||
2316
public/js/dist/bootstrap-table.js
vendored
2316
public/js/dist/bootstrap-table.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -108,8 +108,8 @@
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=393aaa7b368b0670fc42434c8cca7dc7",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=9a35fafd5eb2a66d108b8b669e84f9e1",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=393d720a0f9aba560094fbc8d3b0c0f0",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=5269eb5a6beb74f03387c78938cf17b2",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=bdb65484f5b26ceb5d8ca13331ff2749",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=6660df122e24940d42d03c06775fec7b",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=e0a4b1a80b09333a460973137f39eab4"
|
||||
}
|
||||
|
||||
86
public/vendor/livewire/livewire.esm.js
vendored
86
public/vendor/livewire/livewire.esm.js
vendored
@@ -2557,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;
|
||||
}
|
||||
@@ -2568,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)) {
|
||||
@@ -2707,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() {
|
||||
@@ -2860,7 +2866,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
get raw() {
|
||||
return raw;
|
||||
},
|
||||
version: "3.14.1",
|
||||
version: "3.14.3",
|
||||
flushAndStopDeferringMutations,
|
||||
dontAutoEvaluateFunctions,
|
||||
disableEffectScheduling,
|
||||
@@ -3296,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()));
|
||||
}
|
||||
}
|
||||
@@ -3336,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")) {
|
||||
@@ -3367,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 {
|
||||
@@ -6820,8 +6826,6 @@ var require_module_cjs8 = __commonJS({
|
||||
let toAttributes = Array.from(to.attributes);
|
||||
for (let i = domAttributes.length - 1; i >= 0; i--) {
|
||||
let name = domAttributes[i].name;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (!to.hasAttribute(name)) {
|
||||
from2.removeAttribute(name);
|
||||
}
|
||||
@@ -6829,8 +6833,6 @@ var require_module_cjs8 = __commonJS({
|
||||
for (let i = toAttributes.length - 1; i >= 0; i--) {
|
||||
let name = toAttributes[i].name;
|
||||
let value = toAttributes[i].value;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (from2.getAttribute(name) !== value) {
|
||||
from2.setAttribute(name, value);
|
||||
}
|
||||
@@ -9015,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 = [
|
||||
@@ -9153,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
|
||||
@@ -9184,7 +9224,7 @@ function navigate_default(Alpine19) {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
whenItIsReleased(() => {
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -9198,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();
|
||||
@@ -9206,6 +9246,7 @@ function navigate_default(Alpine19) {
|
||||
preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => {
|
||||
enablePersist && storePersistantElementsForLater((persistedEl) => {
|
||||
packUpPersistedTeleports(persistedEl);
|
||||
packUpPersistedPopovers(persistedEl);
|
||||
});
|
||||
if (shouldPushToHistoryState) {
|
||||
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
|
||||
@@ -9216,6 +9257,7 @@ function navigate_default(Alpine19) {
|
||||
removeAnyLeftOverStaleTeleportTargets(document.body);
|
||||
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
|
||||
unPackPersistedTeleports(persistedEl);
|
||||
unPackPersistedPopovers(persistedEl);
|
||||
});
|
||||
restoreScrollPositionOrScrollToTop();
|
||||
afterNewScriptsAreDoneLoading(() => {
|
||||
@@ -9224,7 +9266,7 @@ function navigate_default(Alpine19) {
|
||||
autofocus && autofocusElementsWithTheAutofocusAttribute();
|
||||
});
|
||||
nowInitializeAlpineOnTheNewPage(Alpine19);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9234,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
|
||||
@@ -9246,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
|
||||
@@ -9254,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) {
|
||||
@@ -9293,7 +9337,7 @@ function preventAlpineFromPickingUpDomChanges(Alpine19, callback) {
|
||||
});
|
||||
});
|
||||
}
|
||||
function fireEventForOtherLibariesToHookInto(name, detail) {
|
||||
function fireEventForOtherLibrariesToHookInto(name, detail) {
|
||||
let event = new CustomEvent(name, {
|
||||
cancelable: true,
|
||||
bubbles: true,
|
||||
@@ -9809,6 +9853,7 @@ function morph2(component, el, html) {
|
||||
},
|
||||
lookahead: false
|
||||
});
|
||||
trigger("morphed", { el, component });
|
||||
}
|
||||
function isntElement(el) {
|
||||
return typeof el.hasAttribute !== "function";
|
||||
@@ -10878,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
|
||||
|
||||
89
public/vendor/livewire/livewire.js
vendored
89
public/vendor/livewire/livewire.js
vendored
@@ -1975,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;
|
||||
}
|
||||
@@ -1986,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)) {
|
||||
@@ -2125,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() {
|
||||
@@ -2278,7 +2284,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
get raw() {
|
||||
return raw;
|
||||
},
|
||||
version: "3.14.1",
|
||||
version: "3.14.3",
|
||||
flushAndStopDeferringMutations,
|
||||
dontAutoEvaluateFunctions,
|
||||
disableEffectScheduling,
|
||||
@@ -3361,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()));
|
||||
}
|
||||
}
|
||||
@@ -3401,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")) {
|
||||
@@ -3432,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 {
|
||||
@@ -4971,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;
|
||||
@@ -7625,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 = [
|
||||
@@ -7763,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
|
||||
@@ -7794,7 +7838,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
whenItIsReleased(() => {
|
||||
let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", {
|
||||
let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", {
|
||||
url: destination,
|
||||
history: false,
|
||||
cached: false
|
||||
@@ -7808,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();
|
||||
@@ -7816,6 +7860,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => {
|
||||
enablePersist && storePersistantElementsForLater((persistedEl) => {
|
||||
packUpPersistedTeleports(persistedEl);
|
||||
packUpPersistedPopovers(persistedEl);
|
||||
});
|
||||
if (shouldPushToHistoryState) {
|
||||
updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination);
|
||||
@@ -7826,6 +7871,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
removeAnyLeftOverStaleTeleportTargets(document.body);
|
||||
enablePersist && putPersistantElementsBack((persistedEl, newStub) => {
|
||||
unPackPersistedTeleports(persistedEl);
|
||||
unPackPersistedPopovers(persistedEl);
|
||||
});
|
||||
restoreScrollPositionOrScrollToTop();
|
||||
afterNewScriptsAreDoneLoading(() => {
|
||||
@@ -7834,7 +7880,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
autofocus && autofocusElementsWithTheAutofocusAttribute();
|
||||
});
|
||||
nowInitializeAlpineOnTheNewPage(Alpine3);
|
||||
fireEventForOtherLibariesToHookInto("alpine:navigated");
|
||||
fireEventForOtherLibrariesToHookInto("alpine:navigated");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7844,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
|
||||
@@ -7856,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
|
||||
@@ -7864,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) {
|
||||
@@ -7903,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,
|
||||
@@ -8212,8 +8260,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let toAttributes = Array.from(to.attributes);
|
||||
for (let i = domAttributes.length - 1; i >= 0; i--) {
|
||||
let name = domAttributes[i].name;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (!to.hasAttribute(name)) {
|
||||
from2.removeAttribute(name);
|
||||
}
|
||||
@@ -8221,8 +8267,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
for (let i = toAttributes.length - 1; i >= 0; i--) {
|
||||
let name = toAttributes[i].name;
|
||||
let value = toAttributes[i].value;
|
||||
if (name === "style")
|
||||
continue;
|
||||
if (from2.getAttribute(name) !== value) {
|
||||
from2.setAttribute(name, value);
|
||||
}
|
||||
@@ -8914,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":"923613aa"}
|
||||
{"/livewire.js":"38dc8241"}
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
142
resources/views/blade/filestable.blade.php
Normal file
142
resources/views/blade/filestable.blade.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<!-- begin redirect submit options -->
|
||||
@props([
|
||||
'filepath',
|
||||
'object',
|
||||
'showfile_routename',
|
||||
'deletefile_routename',
|
||||
])
|
||||
|
||||
<!-- begin non-ajaxed file listing table -->
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
data-cookie-id-table="{{ str_slug($object->name) }}UploadsTable"
|
||||
data-id-table="{{ str_slug($object->name) }}UploadsTable"
|
||||
id="{{ str_slug($object->name) }}}UploadsTable"
|
||||
data-search="true"
|
||||
data-pagination="true"
|
||||
data-side-pagination="client"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="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-license-uploads-{{ str_slug($object->name) }}-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","delete","download","icon"]
|
||||
}'>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-visible="false" data-field="id" data-sortable="true">
|
||||
{{trans('general.id')}}
|
||||
</th>
|
||||
<th data-visible="true" data-field="type" 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-2" data-searchable="true" data-visible="true" data-field="created_by" data-sortable="true">
|
||||
{{ trans('general.created_by') }}
|
||||
</th>
|
||||
<th class="col-md-1" data-searchable="true" data-visible="true" data-field="actions">
|
||||
{{ trans('table.actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($object->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
{{ $file->id }}
|
||||
</td>
|
||||
<td data-sort-value="{{ pathinfo($filepath.$file->filename, PATHINFO_EXTENSION) }}">
|
||||
@if (Storage::exists($filepath.$file->filename))
|
||||
<span class="sr-only">{{ pathinfo($filepath.$file->filename, PATHINFO_EXTENSION) }}</span>
|
||||
<i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true" data-tooltip="true" data-title="{{ pathinfo($filepath.$file->filename, PATHINFO_EXTENSION) }}"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@if (($file->filename) && (Storage::exists($filepath.$file->filename)))
|
||||
@if (Helper::checkUploadIsImage($file->get_src(str_plural(strtolower(class_basename(get_class($object)))))))
|
||||
<a href="{{ route($showfile_routename, [$object->id, $file->id, 'inline' => 'true']) }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ route($showfile_routename, [$object->id, $file->id, 'inline' => 'true']) }}" class="img-thumbnail" style="max-width: 50px;">
|
||||
</a>
|
||||
@else
|
||||
{{ trans('general.preview_not_available') }}
|
||||
@endif
|
||||
@else
|
||||
<x-icon type="x" class="text-danger" />
|
||||
{{ trans('general.file_not_found') }}
|
||||
@endif
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->filename }}
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists($filepath.$file->filename)) ? Storage::size($filepath.$file->filename) : '' }}">
|
||||
{{ (Storage::exists($filepath.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size($filepath.$file->filename)) : '' }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td style="white-space: nowrap;">
|
||||
@if ($file->filename)
|
||||
@if (Storage::exists($filepath.$file->filename))
|
||||
<a href="{{ route($showfile_routename, [$object->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<x-icon type="download" />
|
||||
<span class="sr-only">
|
||||
{{ trans('general.download') }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ StorageHelper::allowSafeInline($filepath.$file->filename) ? route($showfile_routename, [$object->id, $file->id, 'inline' => 'true']) : '#' }}" class="btn btn-sm btn-default{{ StorageHelper::allowSafeInline($filepath.$file->filename) ? '' : ' disabled' }}" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->created_at }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->created_by }}
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn delete-asset btn-danger btn-sm hidden-print" href="{{ route($deletefile_routename, [$object->id, $file->id]) }}" data-content="Are you sure you wish to delete this file?" data-title="{{ trans('general.delete') }} {{ $file->filename }}?">
|
||||
<x-icon type="delete" />
|
||||
<span class="sr-only">{{ trans('general.delete') }}</span>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- end non-ajaxed file listing table -->
|
||||
@@ -20,6 +20,8 @@
|
||||
@include ('partials.forms.edit.quantity')
|
||||
@include ('partials.forms.edit.minimum_quantity')
|
||||
@include ('partials.forms.edit.serial', ['fieldname' => 'serial'])
|
||||
@include ('partials.forms.edit.manufacturer-select', ['translated_name' => trans('general.manufacturer'), 'fieldname' => 'manufacturer_id'])
|
||||
@include ('partials.forms.edit.model_number')
|
||||
@include ('partials.forms.edit.company-select', ['translated_name' => trans('general.company'), 'fieldname' => 'company_id'])
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
|
||||
@include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id'])
|
||||
|
||||
@@ -140,96 +140,14 @@
|
||||
|
||||
@can('components.files', $component)
|
||||
<div class="tab-pane" id="files">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
data-cookie-id-table="componentUploadsTable"
|
||||
data-id-table="componentUploadsTable"
|
||||
id="componentUploadsTable"
|
||||
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-components-uploads-{{ str_slug($component->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 ($component->uploads->count() > 0)
|
||||
@foreach ($component->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
<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('components')))
|
||||
<a href="{{ route('show.componentfile', ['componentId' => $component->id, 'fileId' => $file->id, 'download' => 'false']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show.componentfile', ['componentId' => $component->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/components/'.$file->filename) ? Storage::size('private_uploads/components/'.$file->filename) : '') }}">
|
||||
{{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/components/'.$file->filename) ? Storage::size('private_uploads/components/'.$file->filename) : '') }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
<nobr><a href="{{ route('show.componentfile', [$component->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<x-icon type="download" />
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show.componentfile', [$component->id, $file->id, 'inline' => 'true']) }}" class="btn btn-sm btn-default" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
</nobr>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $file->created_at }}</td>
|
||||
<td>
|
||||
<a class="btn delete-asset btn-danger btn-sm" href="{{ route('delete/componentfile', [$component->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/components/"
|
||||
showfile_routename="show.componentfile"
|
||||
deletefile_routename="delete/componentfile"
|
||||
:object="$component" />
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.tab-pane -->
|
||||
@endcan
|
||||
@@ -243,7 +161,7 @@
|
||||
<div class="col-md-3">
|
||||
@if ($component->image!='')
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 15px;">
|
||||
<a href="{{ Storage::disk('public')->url('components/'.e($component->image)) }}" data-toggle="lightbox">
|
||||
<a href="{{ Storage::disk('public')->url('components/'.e($component->image)) }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ Storage::disk('public')->url('components/'.e($component->image)) }}" class="img-responsive img-thumbnail" alt="{{ $component->name }}"></a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<label for="qty" class="col-md-3 control-label">{{ trans('general.qty') }}</label>
|
||||
<div class="col-md-7 col-sm-12 required">
|
||||
<div class="col-md-2" style="padding-left:0px">
|
||||
<input class="form-control" type="number" name="qty" id="qty" value="1" min="1" max="{{$consumable->numRemaining()}}" maxlength="999999" />
|
||||
<input class="form-control" type="number" name="checkout_qty" id="checkout_qty" value="1" min="1" max="{{$consumable->numRemaining()}}" maxlength="999999" />
|
||||
</div>
|
||||
</div>
|
||||
{!! $errors->first('qty', '<div class="col-md-8 col-md-offset-3"><span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span></div>') !!}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
@if ($consumable->image!='')
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 20px;">
|
||||
<a href="{{ Storage::disk('public')->url('consumables/'.e($consumable->image)) }}" data-toggle="lightbox">
|
||||
<a href="{{ Storage::disk('public')->url('consumables/'.e($consumable->image)) }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ Storage::disk('public')->url('consumables/'.e($consumable->image)) }}" class="img-responsive img-thumbnail" alt="{{ $consumable->name }}"></a>
|
||||
</div>
|
||||
@endif
|
||||
@@ -428,102 +428,18 @@
|
||||
|
||||
|
||||
<div class="tab-pane" id="files">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<x-filestable
|
||||
filepath="private_uploads/consumables/"
|
||||
showfile_routename="show.consumablefile"
|
||||
deletefile_routename="delete/consumablefile"
|
||||
:object="$consumable" />
|
||||
|
||||
<div class="col-md-12 col-sm-12">
|
||||
<div class="table-responsive">
|
||||
|
||||
<table
|
||||
data-cookie-id-table="consumableUploadsTable"
|
||||
data-id-table="consumableUploadsTable"
|
||||
id="consumableUploadsTable"
|
||||
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-consumables-uploads-{{ str_slug($consumable->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 ($consumable->uploads->count() > 0)
|
||||
@foreach ($consumable->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
<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('consumables')))
|
||||
<a href="{{ route('show.consumablefile', ['consumableId' => $consumable->id, 'fileId' => $file->id, 'download' => 'false']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show.consumablefile', ['consumableId' => $consumable->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/consumables/'.$file->filename) ? Storage::size('private_uploads/consumables/'.$file->filename) : '') }}">
|
||||
{{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/consumables/'.$file->filename) ? Storage::size('private_uploads/consumables/'.$file->filename) : '') }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($file->note)) !!}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
<a href="{{ route('show.consumablefile', [$consumable->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.consumablefile', [$consumable->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/consumablefile', [$consumable->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>
|
||||
</div>
|
||||
</div> <!--/ROW-->
|
||||
</div>
|
||||
|
||||
</div><!--/FILES-->
|
||||
|
||||
<div class="tab-pane" id="history">
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<!-- drag handle -->
|
||||
<span class="handle">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</span>
|
||||
</td>
|
||||
@endcan
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<div class="dashboard small-box bg-teal">
|
||||
<div class="inner">
|
||||
<h3>{{ number_format(\App\Models\Asset::AssetsForShow()->count()) }}</h3>
|
||||
<p>{{ strtolower(trans('general.assets')) }}</p>
|
||||
<p>{{ trans('general.assets') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="assets" />
|
||||
@@ -54,7 +54,7 @@
|
||||
<div class="dashboard small-box bg-maroon">
|
||||
<div class="inner">
|
||||
<h3>{{ number_format($counts['license']) }}</h3>
|
||||
<p>{{ strtolower(trans('general.licenses')) }}</p>
|
||||
<p>{{ trans('general.licenses') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="licenses" />
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="dashboard small-box bg-orange">
|
||||
<div class="inner">
|
||||
<h3> {{ number_format($counts['accessory']) }}</h3>
|
||||
<p>{{ strtolower(trans('general.accessories')) }}</p>
|
||||
<p>{{ trans('general.accessories') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="accessories" />
|
||||
@@ -96,7 +96,7 @@
|
||||
<div class="dashboard small-box bg-purple">
|
||||
<div class="inner">
|
||||
<h3> {{ number_format($counts['consumable']) }}</h3>
|
||||
<p>{{ strtolower(trans('general.consumables')) }}</p>
|
||||
<p>{{ trans('general.consumables') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="consumables" />
|
||||
@@ -115,7 +115,7 @@
|
||||
<div class="dashboard small-box bg-yellow">
|
||||
<div class="inner">
|
||||
<h3>{{ number_format($counts['component']) }}</h3>
|
||||
<p>{{ strtolower(trans('general.components')) }}</p>
|
||||
<p>{{ trans('general.components') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="components" />
|
||||
@@ -135,7 +135,7 @@
|
||||
<div class="dashboard small-box bg-light-blue">
|
||||
<div class="inner">
|
||||
<h3>{{ number_format($counts['user']) }}</h3>
|
||||
<p>{{ strtolower(trans('general.people')) }}</p>
|
||||
<p>{{ trans('general.people') }}</p>
|
||||
</div>
|
||||
<div class="icon" aria-hidden="true">
|
||||
<x-icon type="users" />
|
||||
|
||||
@@ -115,5 +115,12 @@
|
||||
|
||||
@section('moar_scripts')
|
||||
@include('partials/assets-assigned')
|
||||
<script nonce="{{ csrf_token() }}">
|
||||
$(function () {
|
||||
//if there's already a user selected, make sure their checked-out assets show up
|
||||
// (if there isn't one, it won't do anything)
|
||||
$('#assigned_user').change();
|
||||
});
|
||||
</script>
|
||||
|
||||
@stop
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<div class="col-md-12 text-center">
|
||||
@if (($asset->image) || (($asset->model) && ($asset->model->image!='')))
|
||||
<div class="text-center col-md-12" style="padding-bottom: 15px;">
|
||||
<a href="{{ ($asset->getImageUrl()) ? $asset->getImageUrl() : null }}" data-toggle="lightbox">
|
||||
<a href="{{ ($asset->getImageUrl()) ? $asset->getImageUrl() : null }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ ($asset->getImageUrl()) ? $asset->getImageUrl() : null }}" class="assetimg img-responsive" alt="{{ $asset->getDisplayNameAttribute() }}">
|
||||
</a>
|
||||
</div>
|
||||
@@ -1362,102 +1362,11 @@
|
||||
<div class="tab-pane fade" id="files">
|
||||
<div class="row{{ ($asset->uploads->count() > 0 ) ? '' : ' hidden-print' }}">
|
||||
<div class="col-md-12">
|
||||
|
||||
@if ($asset->uploads->count() > 0)
|
||||
<table
|
||||
class="table table-striped snipe-table"
|
||||
id="assetFileHistory"
|
||||
data-pagination="true"
|
||||
data-id-table="assetFileHistory"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
data-sortable="true"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="desc"
|
||||
data-sort-name="created_at"
|
||||
data-show-export="true"
|
||||
data-export-options='{
|
||||
"fileName": "export-asset-{{ $asset->id }}-files",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'
|
||||
data-cookie-id-table="assetFileHistory">
|
||||
<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>
|
||||
|
||||
@foreach ($asset->uploads as $file)
|
||||
<tr>
|
||||
<td><i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true"></i></td>
|
||||
<td>
|
||||
@if ( Helper::checkUploadIsImage($file->get_src('assets')))
|
||||
<a href="{{ route('show/assetfile', ['assetId' => $asset->id, 'fileId' =>$file->id]) }}" data-toggle="lightbox" data-type="image" data-title="{{ $file->filename }}" data-footer="{{ Helper::getFormattedDateObject($asset->last_checkout, 'datetime', false) }}">
|
||||
<img src="{{ route('show/assetfile', ['assetId' => $asset->id, 'fileId' =>$file->id]) }}" style="max-width: 50px;">
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (Storage::exists('private_uploads/assets/'.$file->filename))
|
||||
{{ $file->filename }}
|
||||
@else
|
||||
<del>{{ $file->filename }}</del>
|
||||
@endif
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/assets/'.$file->filename) ? Storage::size('private_uploads/assets/'.$file->filename) : '') }}">
|
||||
{{ @Helper::formatFilesizeUnits(Storage::exists('private_uploads/assets/'.$file->filename) ? Storage::size('private_uploads/assets/'.$file->filename) : '') }}
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (($file->filename) && (Storage::exists('private_uploads/assets/'.$file->filename)))
|
||||
<a href="{{ route('show/assetfile', [$asset->id, $file->id, 'download'=>'true']) }}" class="btn btn-sm btn-default hidden-print">
|
||||
<x-icon type="download" />
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show/assetfile', [$asset->id, $file->id, 'inline'=>'true']) }}" class="btn btn-sm btn-default hidden-print" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->created_at)
|
||||
{{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@can('update', \App\Models\Asset::class)
|
||||
<a class="btn delete-asset btn-sm btn-danger btn-sm hidden-print" href="{{ route('delete/assetfile', [$asset->id, $file->id]) }}" data-tooltip="true" data-title="Delete" data-content="{{ trans('general.delete_confirm', ['item' => $file->filename]) }}">
|
||||
<x-icon type="delete" />
|
||||
</a>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-info alert-block hidden-print">
|
||||
<x-icon type="info-circle" />
|
||||
{{ trans('general.no_results') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<x-filestable
|
||||
filepath="private_uploads/assets/"
|
||||
showfile_routename="show/assetfile"
|
||||
deletefile_routename="delete/modelfile"
|
||||
:object="$asset" />
|
||||
</div> <!-- /.col-md-12 -->
|
||||
</div> <!-- /.row -->
|
||||
</div> <!-- /.tab-pane files -->
|
||||
@@ -1467,101 +1376,11 @@
|
||||
<div class="row{{ (($asset->model) && ($asset->model->uploads->count() > 0)) ? '' : ' hidden-print' }}">
|
||||
<div class="col-md-12">
|
||||
|
||||
@if (($asset->model) && ($asset->model->uploads->count() > 0))
|
||||
<table
|
||||
class="table table-striped snipe-table"
|
||||
id="assetModelFileHistory"
|
||||
data-pagination="true"
|
||||
data-id-table="assetModelFileHistory"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
data-sortable="true"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="desc"
|
||||
data-sort-name="created_at"
|
||||
data-show-export="true"
|
||||
data-export-options='{
|
||||
"fileName": "export-assetmodel-{{ $asset->model->id }}-files",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'
|
||||
data-cookie-id-table="assetFileHistory">
|
||||
<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>
|
||||
|
||||
@foreach ($asset->model->uploads as $file)
|
||||
<tr>
|
||||
<td><i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true"></i></td>
|
||||
<td>
|
||||
@if ( Helper::checkUploadIsImage($file->get_src('assetmodels')))
|
||||
<a href="{{ route('show/modelfile', ['modelID' => $asset->model->id, 'fileId' =>$file->id]) }}" data-toggle="lightbox" data-type="image" data-title="{{ $file->filename }}" data-footer="{{ Helper::getFormattedDateObject($asset->last_checkout, 'datetime', false) }}">
|
||||
<img src="{{ route('show/modelfile', ['modelID' => $asset->model->id, 'fileId' =>$file->id]) }}" style="max-width: 50px;">
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (Storage::exists('private_uploads/assetmodels/'.$file->filename))
|
||||
{{ $file->filename }}
|
||||
@else
|
||||
<del>{{ $file->filename }}</del>
|
||||
@endif
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Storage::size('private_uploads/assetmodels/'.$file->filename) : '' }}">
|
||||
{{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/assetmodels/'.$file->filename)) : '' }}
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename)))
|
||||
<a href="{{ route('show/modelfile', [$asset->model->id, $file->id]) }}" class="btn btn-sm btn-default hidden-print">
|
||||
<x-icon type="download" class="hidden-print" />
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show/modelfile', [$asset->model->id, $file->id, 'inline'=>'true']) }}" class="btn btn-sm btn-default hidden-print" target="_blank">
|
||||
<x-icon type="external-link" class="hidden-print" />
|
||||
</a>
|
||||
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->created_at)
|
||||
{{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@can('update', \App\Models\AssetModel::class)
|
||||
<a class="btn delete-asset btn-sm btn-danger btn-sm hidden-print" href="{{ route('delete/modelfile', [$asset->model->id, $file->id]) }}" data-tooltip="true" data-title="Delete" data-content="{{ trans('general.delete_confirm', ['item' => $file->filename]) }}">
|
||||
<x-icon type="delete" class="hidden-print"/></i>
|
||||
</a>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-info alert-block">
|
||||
<x-icon type="info-circle" />
|
||||
{{ trans('general.no_results') }}
|
||||
</div>
|
||||
@endif
|
||||
<x-filestable
|
||||
filepath="private_uploads/assetmodels/"
|
||||
showfile_routename="show/modelfile"
|
||||
deletefile_routename="userfile.destroy"
|
||||
:object="$asset->model" />
|
||||
|
||||
</div> <!-- /.col-md-12 -->
|
||||
</div> <!-- /.row -->
|
||||
|
||||
@@ -279,7 +279,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
|
||||
@endif
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="header">{{ trans('general.quantity_minimum', array('count' => count($alert_items))) }}</li>
|
||||
<li class="header">{{ trans_choice('general.quantity_minimum', count($alert_items)) }}</li>
|
||||
<li>
|
||||
<!-- inner menu: contains the actual data -->
|
||||
<ul class="menu">
|
||||
@@ -287,7 +287,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
|
||||
@for($i = 0; count($alert_items) > $i; $i++)
|
||||
|
||||
<li><!-- Task item -->
|
||||
<a href="{{route($alert_items[$i]['type'].'.show', $alert_items[$i]['id'])}}">
|
||||
<a href="{{ route($alert_items[$i]['type'].'.show', $alert_items[$i]['id'])}}">
|
||||
<h2 class="task_menu">{{ $alert_items[$i]['name'] }}
|
||||
<small class="pull-right">
|
||||
{{ $alert_items[$i]['remaining'] }} {{ trans('general.remaining') }}
|
||||
|
||||
@@ -464,100 +464,13 @@
|
||||
|
||||
@can('licenses.files', $license)
|
||||
<div class="tab-pane" id="files">
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
data-cookie-id-table="licenseUploadsTable"
|
||||
data-id-table="licenseUploadsTable"
|
||||
id="licenseUploadsTable"
|
||||
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-license-uploads-{{ str_slug($license->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 ($license->uploads->count() > 0)
|
||||
@foreach ($license->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
<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 ((Storage::exists('private_uploads/licenses/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('licenses'))))
|
||||
<a href="{{ route('show.licensefile', ['licenseId' => $license->id, 'fileId' => $file->id, 'download' => 'false']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show.licensefile', ['licenseId' => $license->id, 'fileId' => $file->id]) }}" class="img-thumbnail" style="max-width: 50px;"></a>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (Storage::exists('private_uploads/licenses/'.$file->filename))
|
||||
{{ $file->filename }}
|
||||
@else
|
||||
<del>{{ $file->filename }}</del>
|
||||
@endif
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/licenses/'.$file->filename)) ? Storage::size('private_uploads/licenses/'.$file->filename) : '' }}">
|
||||
{{ (Storage::exists('private_uploads/licenses/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/licenses/'.$file->filename)) : '' }}
|
||||
</td>
|
||||
<x-filestable
|
||||
filepath="private_uploads/licenses/"
|
||||
showfile_routename="show.licensefile"
|
||||
deletefile_routename="delete/licensefile"
|
||||
:object="$license" />
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
<a href="{{ route('show.licensefile', [$license->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<x-icon type="download"/>
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show.licensefile', [$license->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/licensefile', [$license->id, $file->id]) }}" data-content="{{ trans('general.delete_confirm', ['item' => $file->filename]) }}" data-title="{{ trans('general.delete') }}">
|
||||
<x-icon type="delete" />
|
||||
<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>
|
||||
</div> <!-- /.tab-pane -->
|
||||
@endcan
|
||||
|
||||
|
||||
@@ -79,6 +79,19 @@
|
||||
{{ trans('general.consumables') }}
|
||||
{!! ($manufacturer->consumables->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($manufacturer->consumables->count()).'</badge>' : '' !!}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#components" data-toggle="tab">
|
||||
|
||||
<span class="hidden-lg hidden-md">
|
||||
<x-icon type="components" class="fa-2x" />
|
||||
</span>
|
||||
<span class="hidden-xs hidden-sm">
|
||||
{{ trans('general.components') }}
|
||||
{!! ($manufacturer->components->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($manufacturer->components->count()).'</badge>' : '' !!}
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</li>
|
||||
@@ -186,12 +199,35 @@
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.consumables.index', ['manufacturer_id' => $manufacturer->id]) }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-manufacturers-{{ str_slug($manufacturer->name) }}-consumabled-{{ date('Y-m-d') }}",
|
||||
"fileName": "export-manufacturers-{{ str_slug($manufacturer->name) }}-consumables-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
</table>
|
||||
|
||||
</div> <!-- /.tab-pan consumables-->
|
||||
|
||||
<div class="tab-pane fade" id="components">
|
||||
|
||||
<table
|
||||
data-columns="{{ \App\Presenters\ComponentPresenter::dataTableLayout() }}"
|
||||
data-cookie-id-table="componentsTable"
|
||||
data-pagination="true"
|
||||
data-id-table="componentsTable"
|
||||
data-search="true"
|
||||
data-show-footer="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
id="componentsTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.components.index', ['manufacturer_id' => $manufacturer->id]) }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-manufacturers-{{ str_slug($manufacturer->name) }}-components-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
</table>
|
||||
|
||||
</div> <!-- /.tab-pan consumables-->
|
||||
|
||||
|
||||
@@ -107,99 +107,11 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
@if ($model->uploads->count() > 0)
|
||||
<table
|
||||
class="table table-striped snipe-table"
|
||||
id="modelFileHistory"
|
||||
data-pagination="true"
|
||||
data-id-table="modelFileHistory"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
data-sortable="true"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="desc"
|
||||
data-sort-name="created_at"
|
||||
data-show-export="true"
|
||||
data-export-options='{
|
||||
"fileName": "export-asset-{{ $model->id }}-files",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'
|
||||
data-cookie-id-table="assetFileHistory">
|
||||
<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>
|
||||
|
||||
@foreach ($model->uploads as $file)
|
||||
<tr>
|
||||
<td><i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true"></i></td>
|
||||
<td>
|
||||
@if ((Storage::exists('private_uploads/assetmodels/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('assetmodels'))))
|
||||
<a href="{{ route('show/modelfile', ['modelID' => $model->id, 'fileId' => $file->id]) }}" data-toggle="lightbox" data-type="image" data-title="{{ $file->filename }}">
|
||||
<img src="{{ route('show/modelfile', ['modelID' => $model->id, 'fileId' =>$file->id]) }}" style="max-width: 50px;">
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (Storage::exists('private_uploads/assetmodels/'.$file->filename))
|
||||
{{ $file->filename }}
|
||||
@else
|
||||
<del>{{ $file->filename }}</del>
|
||||
@endif
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Storage::size('private_uploads/assetmodels/'.$file->filename) : '' }}">
|
||||
{{ (Storage::exists('private_uploads/assetmodels/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/assetmodels/'.$file->filename)) : '' }}
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td style="white-space: nowrap">
|
||||
@if (($file->filename) && (Storage::exists('private_uploads/assetmodels/'.$file->filename)))
|
||||
<a href="{{ route('show/modelfile', [$model->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show/modelfile', [$model->id, $file->id, 'inline'=>'true']) }}" class="btn btn-sm btn-default" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->created_at)
|
||||
{{ Helper::getFormattedDateObject($file->created_at, 'datetime', false) }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@can('update', \App\Models\AssetModel::class)
|
||||
<a class="btn delete-asset btn-sm btn-danger btn-sm" href="{{ route('delete/assetfile', [$model->id, $file->id]) }}" data-tooltip="true" data-title="Delete" data-content="{{ trans('general.delete_confirm', ['item' => $file->filename]) }}"><i class="fas fa-trash icon-white" aria-hidden="true"></i></a>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-info alert-block">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
{{ trans('general.no_results') }}
|
||||
</div>
|
||||
@endif
|
||||
<x-filestable
|
||||
filepath="private_uploads/assetmodels/"
|
||||
showfile_routename="show/modelfile"
|
||||
deletefile_routename="delete/modelfile"
|
||||
:object="$model" />
|
||||
|
||||
</div> <!-- /.col-md-12 -->
|
||||
</div> <!-- /.row -->
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
| **{{ trans('mail.checkout_date') }}** | {{ $checkout_date }} |
|
||||
@endif
|
||||
| **{{ trans('general.consumable') }}** | {{ $item->name }} |
|
||||
@if (isset($qty))
|
||||
| **{{ trans('general.qty') }}** | {{ $qty }} |
|
||||
@endif
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
|
||||
@@ -16,17 +16,20 @@
|
||||
</label>
|
||||
<select name="bulk_actions" class="form-control select2" aria-label="bulk_actions" style="min-width: 350px;">
|
||||
@if((isset($status)) && ($status == 'Deleted'))
|
||||
@can('delete', \App\Models\Asset::class)
|
||||
<option value="restore">{{trans('button.restore')}}</option>
|
||||
@endcan
|
||||
@can('delete', \App\Models\Asset::class)
|
||||
<option value="restore">{{trans('button.restore')}}</option>
|
||||
@endcan
|
||||
@else
|
||||
@can('update', \App\Models\Asset::class)
|
||||
<option value="edit">{{ trans('button.edit') }}</option>
|
||||
@endcan
|
||||
@can('delete', \App\Models\Asset::class)
|
||||
<option value="delete">{{ trans('button.delete') }}</option>
|
||||
@endcan
|
||||
<option value="labels" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=l" : ''}}>{{ trans_choice('button.generate_labels', 2) }}</option>
|
||||
@can('update', \App\Models\Asset::class)
|
||||
<option value="edit">{{ trans('button.edit') }}</option>
|
||||
@endcan
|
||||
@can('checkout', \App\Models\Asset::class)
|
||||
<option value="checkout">{{ trans('general.bulk_checkout') }}</option>
|
||||
@endcan
|
||||
@can('delete', \App\Models\Asset::class)
|
||||
<option value="delete">{{ trans('button.delete') }}</option>
|
||||
@endcan
|
||||
<option value="labels" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=l" : ''}}>{{ trans_choice('button.generate_labels', 2) }}</option>
|
||||
@endif
|
||||
</select>
|
||||
|
||||
|
||||
@@ -98,52 +98,14 @@
|
||||
},
|
||||
formatNoMatches: function () {
|
||||
return '{{ trans('table.no_matching_records') }}';
|
||||
},
|
||||
{{--onDrop: function (table, row) {--}}
|
||||
{{-- var rows = table.tBodies[0].rows;--}}
|
||||
{{-- var debugStr = "Row dropped was " + row.id + ". New order: ";--}}
|
||||
{{-- for (var i = 0; i < rows.length; i++) {--}}
|
||||
{{-- debugStr += rows[i].id + " ";--}}
|
||||
{{-- }--}}
|
||||
{{-- --}}
|
||||
{{-- $.ajax({--}}
|
||||
{{-- url: '{{ route('api.reorder') }}',--}}
|
||||
{{-- type: 'POST',--}}
|
||||
{{-- data: {--}}
|
||||
{{-- ids: rows.map(function (row) {--}}
|
||||
{{-- return row.id;--}}
|
||||
{{-- })--}}
|
||||
{{--},--}}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(function () {
|
||||
var debugStr = '';
|
||||
$('.snipe-table').on('reorder-row.bs.table', function (e, data, row){
|
||||
console.log('All Data:');
|
||||
console.log(data);
|
||||
console.log('Dragged Row Data:');
|
||||
console.dir(row);
|
||||
console.log('E:');
|
||||
console.dir(e);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
debugStr += data[i].id + " ";
|
||||
}
|
||||
alert('re-order ID: ' + row.id + ' to ' + debugStr);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// function onReorderDrop (rows, row) {
|
||||
// var rows = table.tBodies[0].rows;
|
||||
// var debugStr = "Row dropped was " + row.id + ". New order: ";
|
||||
// for (var i = 0; i < rows.length; i++) {
|
||||
// debugStr += rows[i].id + " ";
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
@include ('partials.forms.edit.name', ['translated_name' => trans('general.name')])
|
||||
|
||||
<!-- Label type -->
|
||||
<div class="form-group{{ $errors->has('statuslabel_types') ? ' has-error' : '' }}">
|
||||
<div class="form-group{{ $errors->has('status_type') ? ' has-error' : '' }}">
|
||||
<label for="statuslabel_types" class="col-md-3 control-label">
|
||||
{{ trans('admin/statuslabels/table.status_type') }}
|
||||
</label>
|
||||
<div class="col-md-7 required">
|
||||
{{ Form::select('statuslabel_types', $statuslabel_types, $item->getStatuslabelType(), array('class'=>'select2', 'style'=>'width: 100%; min-width:400px', 'aria-label'=>'statuslabel_types')) }}
|
||||
{!! $errors->first('statuslabel_types', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
{{ Form::select('status_type', $status_types, $item->status_type, array('class'=>'select2', 'style'=>'width: 100%; min-width:400px', 'aria-label'=>'status_type')) }}
|
||||
{!! $errors->first('status_type', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
|
||||
|
||||
<table
|
||||
data-columns="{{ \App\Presenters\StatusLabelPresenter::dataTableLayout() }}"
|
||||
data-cookie-id-table="statuslabelsTable"
|
||||
@@ -37,8 +36,6 @@
|
||||
data-show-export="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-refresh="true"
|
||||
data-use-row-attr-func="true"
|
||||
data-reorderable-rows="true"
|
||||
data-sort-order="asc"
|
||||
data-sort-name="name"
|
||||
id="statuslabelsTable"
|
||||
|
||||
@@ -971,102 +971,11 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12 col-sm-12">
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
data-cookie-id-table="userUploadsTable"
|
||||
data-id-table="userUploadsTable"
|
||||
id="userUploadsTable"
|
||||
data-search="true"
|
||||
data-pagination="true"
|
||||
data-side-pagination="client"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="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-license-uploads-{{ str_slug($user->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>
|
||||
@foreach ($user->uploads as $file)
|
||||
<tr>
|
||||
<td>
|
||||
<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) && (Storage::exists('private_uploads/users/'.$file->filename)))
|
||||
@if (Helper::checkUploadIsImage($file->get_src('users')))
|
||||
<a href="{{ route('show/userfile', [$user->id, $file->id, 'inline' => 'true']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show/userfile', [$user->id, $file->id, 'inline' => 'true']) }}" class="img-thumbnail" style="max-width: 50px;"></a>
|
||||
@else
|
||||
{{ trans('general.preview_not_available') }}
|
||||
@endif
|
||||
@else
|
||||
<x-icon type="x" class="text-danger" />
|
||||
{{ trans('general.file_not_found') }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->filename }}
|
||||
</td>
|
||||
<td data-value="{{ (Storage::exists('private_uploads/users/'.$file->filename)) ? Storage::size('private_uploads/users/'.$file->filename) : '' }}">
|
||||
{{ (Storage::exists('private_uploads/users/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/users/'.$file->filename)) : '' }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if ($file->note)
|
||||
{{ $file->note }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
@if (Storage::exists('private_uploads/users/'.$file->filename))
|
||||
<a href="{{ route('show/userfile', [$user->id, $file->id]) }}" class="btn btn-sm btn-default">
|
||||
<x-icon type="download" />
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('show/userfile', [$user->id, $file->id, 'inline' => 'true']) }}" class="btn btn-sm btn-default" target="_blank">
|
||||
<x-icon type="external-link" />
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $file->created_at }}</td>
|
||||
|
||||
<td>
|
||||
<a class="btn delete-asset btn-danger btn-sm hidden-print" href="{{ route('userfile.destroy', [$user->id, $file->id]) }}" data-content="Are you sure you wish to delete this file?" data-title="{{ trans('general.delete') }} {{ $file->filename }}?">
|
||||
<x-icon type="delete" />
|
||||
<span class="sr-only">{{ trans('general.delete') }}</span>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<x-filestable
|
||||
filepath="private_uploads/users/"
|
||||
showfile_routename="show/userfile"
|
||||
deletefile_routename="userfile.destroy"
|
||||
:object="$user" />
|
||||
</div>
|
||||
</div> <!--/ROW-->
|
||||
</div><!--/FILES-->
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Accessories\Ui;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Category;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\Support\ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CreateAccessoryWithFullMultipleCompanySupportTest extends TestCase
|
||||
{
|
||||
use ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
|
||||
#[DataProvider('dataForFullMultipleCompanySupportTesting')]
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping($data)
|
||||
{
|
||||
['actor' => $actor, 'company_attempting_to_associate' => $company, 'assertions' => $assertions] = $data();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAs($actor)
|
||||
->post(route('accessories.store'), [
|
||||
'redirect_option' => 'index',
|
||||
'name' => 'My Cool Accessory',
|
||||
'qty' => '1',
|
||||
'category_id' => Category::factory()->create()->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$accessory = Accessory::withoutGlobalScopes()->where([
|
||||
'name' => 'My Cool Accessory',
|
||||
])->sole();
|
||||
|
||||
$assertions($accessory);
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,23 @@ class AssetIndexTest extends TestCase
|
||||
{
|
||||
public function testAssetApiIndexReturnsExpectedAssets()
|
||||
{
|
||||
Asset::factory()->count(3)->create();
|
||||
$this->withoutExceptionHandling();
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
if ($assets = Asset::factory()->count(3)->make()) {
|
||||
//dd($assets);
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
\Log::error($asset->asset_tag);
|
||||
\Log::error($asset->assetstatus->name);
|
||||
\Log::error($asset->assetstatus->status_type);
|
||||
}
|
||||
} else {
|
||||
\Log::error('No created');
|
||||
}
|
||||
|
||||
|
||||
|
||||
$response = $this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->getJson(
|
||||
route('api.assets.index', [
|
||||
'sort' => 'name',
|
||||
@@ -29,6 +43,7 @@ class AssetIndexTest extends TestCase
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc());
|
||||
\Log::error($response);
|
||||
}
|
||||
|
||||
public function testAssetApiIndexReturnsDisplayUpcomingAuditsDue()
|
||||
|
||||
@@ -560,6 +560,9 @@ class StoreAssetTest extends TestCase
|
||||
$this->assertTrue($asset->assignedAssets()->find($response['payload']['id'])->is($apiAsset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://app.shortcut.com/grokability/story/24475
|
||||
*/
|
||||
public function testCompanyIdNeedsToBeInteger()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->createAssets()->create())
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Assets\Api;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\Support\ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
use Tests\TestCase;
|
||||
|
||||
class StoreAssetWithFullMultipleCompanySupportTest extends TestCase
|
||||
{
|
||||
use ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
|
||||
/**
|
||||
* @link https://github.com/snipe/snipe-it/issues/15654
|
||||
*/
|
||||
#[DataProvider('dataForFullMultipleCompanySupportTesting')]
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping($data)
|
||||
{
|
||||
['actor' => $actor, 'company_attempting_to_associate' => $company, 'assertions' => $assertions] = $data();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$response = $this->actingAsForApi($actor)
|
||||
->postJson(route('api.assets.store'), [
|
||||
'asset_tag' => 'random_string',
|
||||
'company_id' => $company->id,
|
||||
'model_id' => AssetModel::factory()->create()->id,
|
||||
'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
|
||||
]);
|
||||
|
||||
$asset = Asset::withoutGlobalScopes()->findOrFail($response['payload']['id']);
|
||||
|
||||
$assertions($asset);
|
||||
}
|
||||
|
||||
#[DataProvider('dataForFullMultipleCompanySupportTesting')]
|
||||
public function testHandlesCompanyIdBeingString($data)
|
||||
{
|
||||
['actor' => $actor, 'company_attempting_to_associate' => $company, 'assertions' => $assertions] = $data();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$response = $this->actingAsForApi($actor)
|
||||
->postJson(route('api.assets.store'), [
|
||||
'asset_tag' => 'random_string',
|
||||
'company_id' => (string) $company->id,
|
||||
'model_id' => AssetModel::factory()->create()->id,
|
||||
'status_id' => Statuslabel::factory()->readyToDeploy()->create()->id,
|
||||
]);
|
||||
|
||||
$asset = Asset::withoutGlobalScopes()->findOrFail($response['payload']['id']);
|
||||
|
||||
$assertions($asset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Assets\Ui;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\Support\ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
use Tests\TestCase;
|
||||
|
||||
class StoreAssetWithFullMultipleCompanySupportTest extends TestCase
|
||||
{
|
||||
use ProvidesDataForFullMultipleCompanySupportTesting;
|
||||
|
||||
#[DataProvider('dataForFullMultipleCompanySupportTesting')]
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping($data)
|
||||
{
|
||||
['actor' => $actor, 'company_attempting_to_associate' => $company, 'assertions' => $assertions] = $data();
|
||||
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
$this->actingAs($actor)
|
||||
->post(route('hardware.store'), [
|
||||
'asset_tags' => ['1' => '1234'],
|
||||
'model_id' => AssetModel::factory()->create()->id,
|
||||
'status_id' => Statuslabel::factory()->create()->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$asset = Asset::where('asset_tag', '1234')->sole();
|
||||
|
||||
$assertions($asset);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user