mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-09 23:24:06 -08:00
Merge branch 'develop' into chore/sc-24808
This commit is contained in:
commit
aa2632fe46
|
@ -29,6 +29,7 @@ RUN apk add --no-cache \
|
|||
php81-sodium \
|
||||
php81-redis \
|
||||
php81-pecl-memcached \
|
||||
php81-exif \
|
||||
curl \
|
||||
wget \
|
||||
vim \
|
||||
|
|
17
TESTING.md
17
TESTING.md
|
@ -45,8 +45,21 @@ DB_PASSWORD={}
|
|||
|
||||
Now you are ready to run the entire test suite from your terminal:
|
||||
|
||||
`php artisan test`
|
||||
```shell
|
||||
php artisan test
|
||||
````
|
||||
|
||||
To run individual test files, you can pass the path to the test that you want to run:
|
||||
|
||||
`php artisan test tests/Unit/AccessoryTest.php`
|
||||
```shell
|
||||
php artisan test tests/Unit/AccessoryTest.php
|
||||
```
|
||||
|
||||
Some tests, like ones concerning LDAP, are marked with the `@group` annotation. Those groups can be run, or excluded, using the `--group` or `--exclude-group` flags:
|
||||
|
||||
```shell
|
||||
php artisan test --group=ldap
|
||||
|
||||
php artisan test --exclude-group=ldap
|
||||
```
|
||||
This can be helpful if a set of tests are failing because you don't have an extension, like LDAP, installed.
|
||||
|
|
|
@ -5,6 +5,151 @@ namespace App\Console\Commands;
|
|||
use Illuminate\Console\Command;
|
||||
use ZipArchive;
|
||||
|
||||
class SQLStreamer {
|
||||
private $input;
|
||||
private $output;
|
||||
// embed the prefix here?
|
||||
public ?string $prefix;
|
||||
|
||||
private bool $reading_beginning_of_line = true;
|
||||
|
||||
public static $buffer_size = 1024 * 1024; // use a 1MB buffer, ought to work fine for most cases?
|
||||
|
||||
public array $tablenames = [];
|
||||
private bool $should_guess = false;
|
||||
private bool $statement_is_permitted = false;
|
||||
|
||||
public function __construct($input, $output, string $prefix = null)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
public function parse_sql(string $line): string {
|
||||
// take into account the 'start of line or not' setting as an instance variable?
|
||||
// 'continuation' lines for a permitted statement are PERMITTED.
|
||||
if($this->statement_is_permitted && $line[0] === ' ') {
|
||||
return $line;
|
||||
}
|
||||
|
||||
$table_regex = '`?([a-zA-Z0-9_]+)`?';
|
||||
$allowed_statements = [
|
||||
"/^(DROP TABLE (?:IF EXISTS )?)`$table_regex(.*)$/" => false,
|
||||
"/^(CREATE TABLE )$table_regex(.*)$/" => true, //sets up 'continuation'
|
||||
"/^(LOCK TABLES )$table_regex(.*)$/" => false,
|
||||
"/^(INSERT INTO )$table_regex(.*)$/" => false,
|
||||
"/^UNLOCK TABLES/" => false,
|
||||
// "/^\\) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/" => false, // FIXME not sure what to do here?
|
||||
"/^\\)[a-zA-Z0-9_= ]*;$/" => false
|
||||
// ^^^^^^ that bit should *exit* the 'perimitted' black
|
||||
];
|
||||
|
||||
foreach($allowed_statements as $statement => $statechange) {
|
||||
// $this->info("Checking regex: $statement...\n");
|
||||
$matches = [];
|
||||
if (preg_match($statement,$line,$matches)) {
|
||||
$this->statement_is_permitted = $statechange;
|
||||
// matches are: 1 => first part of the statement, 2 => tablename, 3 => rest of statement
|
||||
// (with of course 0 being "the whole match")
|
||||
if (@$matches[2]) {
|
||||
// print "Found a tablename! It's: ".$matches[2]."\n";
|
||||
if ($this->should_guess) {
|
||||
@$this->tablenames[$matches[2]] += 1;
|
||||
continue; //oh? FIXME
|
||||
} else {
|
||||
$cleaned_tablename = \DB::getTablePrefix().preg_replace('/^'.$this->prefix.'/','',$matches[2]);
|
||||
$line = preg_replace($statement,'$1`'.$cleaned_tablename.'`$3' , $line);
|
||||
}
|
||||
} else {
|
||||
// no explicit tablename in this one, leave the line alone
|
||||
}
|
||||
//how do we *replace* the tablename?
|
||||
// print "RETURNING LINE: $line";
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
// all that is not allowed is denied.
|
||||
return "";
|
||||
}
|
||||
|
||||
//this is used in exactly *TWO* places, and in both cases should return a prefix I think?
|
||||
// first - if you do the --sanitize-only one (which is mostly for testing/development)
|
||||
// next - when you run *without* a guessed prefix, this is run first to figure out the prefix
|
||||
// I think we have to *duplicate* the call to be able to run it again?
|
||||
public static function guess_prefix($input):string
|
||||
{
|
||||
$parser = new self($input, null);
|
||||
$parser->should_guess = true;
|
||||
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
||||
|
||||
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
|
||||
//can't use 'users' because the 'accessories_users' table?
|
||||
// can't use 'assets' because 'ver1_components_assets'
|
||||
foreach($check_tables as $check_table => $_ignore) {
|
||||
foreach ($parser->tablenames as $tablename => $_count) {
|
||||
// print "Comparing $tablename to $check_table\n";
|
||||
if (str_ends_with($tablename,$check_table)) {
|
||||
// print "Found one!\n";
|
||||
$check_tables[$check_table] = substr($tablename,0,-strlen($check_table));
|
||||
}
|
||||
}
|
||||
}
|
||||
$guessed_prefix = null;
|
||||
foreach ($check_tables as $clean_table => $prefix_guess) {
|
||||
if(is_null($prefix_guess)) {
|
||||
print("Couldn't find table $clean_table\n");
|
||||
die();
|
||||
}
|
||||
if(is_null($guessed_prefix)) {
|
||||
$guessed_prefix = $prefix_guess;
|
||||
} else {
|
||||
if ($guessed_prefix != $prefix_guess) {
|
||||
print("Prefix mismatch! Had guessed $guessed_prefix but got $prefix_guess\n");
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $guessed_prefix;
|
||||
|
||||
}
|
||||
|
||||
public function line_aware_piping(): int
|
||||
{
|
||||
$bytes_read = 0;
|
||||
if (! $this->input) {
|
||||
throw new \Exception("No Input available for line_aware_piping");
|
||||
}
|
||||
|
||||
while (($buffer = fgets($this->input, SQLStreamer::$buffer_size)) !== false) {
|
||||
$bytes_read += strlen($buffer);
|
||||
if ($this->reading_beginning_of_line) {
|
||||
// \Log::debug("Buffer is: '$buffer'");
|
||||
$cleaned_buffer = $this->parse_sql($buffer);
|
||||
if ($this->output) {
|
||||
$bytes_written = fwrite($this->output, $cleaned_buffer);
|
||||
|
||||
if ($bytes_written === false) {
|
||||
throw new \Exception("Unable to write to pipe");
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we got a newline at the end of this, then the _next_ read is the beginning of a line
|
||||
if($buffer[strlen($buffer)-1] === "\n") {
|
||||
$this->reading_beginning_of_line = true;
|
||||
} else {
|
||||
$this->reading_beginning_of_line = false;
|
||||
}
|
||||
|
||||
}
|
||||
return $bytes_read;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RestoreFromBackup extends Command
|
||||
{
|
||||
/**
|
||||
|
@ -12,10 +157,13 @@ class RestoreFromBackup extends Command
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
// FIXME - , stripping prefixes and nonstandard SQL statements. Without --prefix, guess and return the correct prefix to strip
|
||||
protected $signature = 'snipeit:restore
|
||||
{--force : Skip the danger prompt; assuming you enter "y"}
|
||||
{filename : The zip file to be migrated}
|
||||
{--no-progress : Don\'t show a progress bar}';
|
||||
{--no-progress : Don\'t show a progress bar}
|
||||
{--sanitize-guess-prefix : Guess and output the table-prefix needed to "sanitize" the SQL}
|
||||
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
@ -34,8 +182,6 @@ class RestoreFromBackup extends Command
|
|||
parent::__construct();
|
||||
}
|
||||
|
||||
public static $buffer_size = 1024 * 1024; // use a 1MB buffer, ought to work fine for most cases?
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
|
@ -55,7 +201,7 @@ class RestoreFromBackup extends Command
|
|||
return $this->error('Missing required filename');
|
||||
}
|
||||
|
||||
if (! $this->option('force') && ! $this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
|
||||
if (! $this->option('force') && ! $this->option('sanitize-guess-prefix') && ! $this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
|
||||
return $this->error('Data loss not confirmed');
|
||||
}
|
||||
|
||||
|
@ -196,7 +342,6 @@ class RestoreFromBackup extends Command
|
|||
}
|
||||
$boring_files[] = $raw_path; //if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
|
||||
} // end of pre-processing the ZIP file for-loop
|
||||
|
||||
// print_r($interesting_files);exit(-1);
|
||||
|
||||
if (count($sqlfiles) != 1) {
|
||||
|
@ -208,6 +353,17 @@ class RestoreFromBackup extends Command
|
|||
//older Snipe-IT installs don't have the db-dumps subdirectory component
|
||||
}
|
||||
|
||||
$sql_stat = $za->statIndex($sqlfile_indices[0]);
|
||||
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
|
||||
$sql_contents = $za->getStream($sql_stat['name']); // maybe copy *THIS* thing?
|
||||
|
||||
// OKAY, now that we *found* the sql file if we're doing just the guess-prefix thing, we can do that *HERE* I think?
|
||||
if ($this->option('sanitize-guess-prefix')) {
|
||||
$prefix = SQLStreamer::guess_prefix($sql_contents);
|
||||
$this->line($prefix);
|
||||
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
|
||||
}
|
||||
|
||||
//how to invoke the restore?
|
||||
$pipes = [];
|
||||
|
||||
|
@ -228,6 +384,7 @@ class RestoreFromBackup extends Command
|
|||
return $this->error('Unable to invoke mysql via CLI');
|
||||
}
|
||||
|
||||
// I'm not sure about these?
|
||||
stream_set_blocking($pipes[1], false); // use non-blocking reads for stdout
|
||||
stream_set_blocking($pipes[2], false); // use non-blocking reads for stderr
|
||||
|
||||
|
@ -238,9 +395,9 @@ class RestoreFromBackup extends Command
|
|||
|
||||
//$sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
|
||||
|
||||
$sql_stat = $za->statIndex($sqlfile_indices[0]);
|
||||
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
|
||||
$sql_contents = $za->getStream($sql_stat['name']);
|
||||
// FIXME - this feels like it wants to go somewhere else?
|
||||
// and it doesn't seem 'right' - if you can't get a stream to the .sql file,
|
||||
// why do we care what's happening with pipes and stdout and stderr?!
|
||||
if ($sql_contents === false) {
|
||||
$stdout = fgets($pipes[1]);
|
||||
$this->info($stdout);
|
||||
|
@ -249,10 +406,12 @@ class RestoreFromBackup extends Command
|
|||
|
||||
return false;
|
||||
}
|
||||
$bytes_read = 0;
|
||||
|
||||
try {
|
||||
while (($buffer = fgets($sql_contents, self::$buffer_size)) !== false) {
|
||||
if ( $this->option('sanitize-with-prefix') === null) {
|
||||
// "Legacy" direct-piping
|
||||
$bytes_read = 0;
|
||||
while (($buffer = fgets($sql_contents, SQLStreamer::$buffer_size)) !== false) {
|
||||
$bytes_read += strlen($buffer);
|
||||
// \Log::debug("Buffer is: '$buffer'");
|
||||
$bytes_written = fwrite($pipes[0], $buffer);
|
||||
|
@ -261,8 +420,13 @@ class RestoreFromBackup extends Command
|
|||
throw new Exception("Unable to write to pipe");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$sql_importer = new SQLStreamer($sql_contents, $pipes[0], $this->option('sanitize-with-prefix'));
|
||||
$bytes_read = $sql_importer->line_aware_piping();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("Error during restore!!!! ".$e->getMessage());
|
||||
// FIXME - put these back and/or put them in the right places?!
|
||||
$err_out = fgets($pipes[1]);
|
||||
$err_err = fgets($pipes[2]);
|
||||
\Log::error("Error OUTPUT: ".$err_out);
|
||||
|
@ -271,7 +435,6 @@ class RestoreFromBackup extends Command
|
|||
$this->error($err_err);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!feof($sql_contents) || $bytes_read == 0) {
|
||||
return $this->error("Not at end of file for sql file, or zero bytes read. aborting!");
|
||||
}
|
||||
|
@ -303,7 +466,7 @@ class RestoreFromBackup extends Command
|
|||
$fp = $za->getStream($ugly_file_name);
|
||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
|
||||
while (($buffer = fgets($fp, self::$buffer_size)) !== false) {
|
||||
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
|
||||
fwrite($migrated_file, $buffer);
|
||||
}
|
||||
fclose($migrated_file);
|
||||
|
|
|
@ -36,7 +36,8 @@ class AssetMaintenancesController extends Controller
|
|||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
$maintenances = AssetMaintenance::select('asset_maintenances.*')->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'admin');
|
||||
$maintenances = AssetMaintenance::select('asset_maintenances.*')
|
||||
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'admin');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$maintenances = $maintenances->TextSearch($request->input('search'));
|
||||
|
@ -47,7 +48,7 @@ class AssetMaintenancesController extends Controller
|
|||
}
|
||||
|
||||
if ($request->filled('supplier_id')) {
|
||||
$maintenances->where('supplier_id', '=', $request->input('supplier_id'));
|
||||
$maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
|
||||
}
|
||||
|
||||
if ($request->filled('asset_maintenance_type')) {
|
||||
|
@ -70,10 +71,13 @@ class AssetMaintenancesController extends Controller
|
|||
'notes',
|
||||
'asset_tag',
|
||||
'asset_name',
|
||||
'serial',
|
||||
'user_id',
|
||||
'supplier',
|
||||
'is_warranty',
|
||||
'status_label',
|
||||
];
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
|
||||
|
@ -90,6 +94,12 @@ class AssetMaintenancesController extends Controller
|
|||
case 'asset_name':
|
||||
$maintenances = $maintenances->OrderByAssetName($order);
|
||||
break;
|
||||
case 'serial':
|
||||
$maintenances = $maintenances->OrderByAssetSerial($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$maintenances = $maintenances->OrderStatusName($order);
|
||||
break;
|
||||
default:
|
||||
$maintenances = $maintenances->orderBy($sort, $order);
|
||||
break;
|
||||
|
|
|
@ -903,6 +903,13 @@ class AssetsController extends Controller
|
|||
$originalValues['action_date'] = $checkin_at;
|
||||
}
|
||||
|
||||
if(!empty($asset->licenseseats->all())){
|
||||
foreach ($asset->licenseseats as $seat){
|
||||
$seat->assigned_to = null;
|
||||
$seat->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($asset->save()) {
|
||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
|
|
|
@ -25,9 +25,27 @@ class LocationsController extends Controller
|
|||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$allowed_columns = [
|
||||
'id', 'name', 'address', 'address2', 'city', 'state', 'country', 'zip', 'created_at',
|
||||
'updated_at', 'manager_id', 'image',
|
||||
'assigned_assets_count', 'users_count', 'assets_count','assigned_assets_count', 'assets_count', 'rtd_assets_count', 'currency', 'ldap_ou', ];
|
||||
'id',
|
||||
'name',
|
||||
'address',
|
||||
'address2',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'manager_id',
|
||||
'image',
|
||||
'assigned_assets_count',
|
||||
'users_count',
|
||||
'assets_count',
|
||||
'assigned_assets_count',
|
||||
'assets_count',
|
||||
'rtd_assets_count',
|
||||
'currency',
|
||||
'ldap_ou',
|
||||
];
|
||||
|
||||
$locations = Location::with('parent', 'manager', 'children')->select([
|
||||
'locations.id',
|
||||
|
@ -50,6 +68,7 @@ class LocationsController extends Controller
|
|||
])->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
|
@ -80,6 +99,10 @@ class LocationsController extends Controller
|
|||
$locations->where('locations.country', '=', $request->input('country'));
|
||||
}
|
||||
|
||||
if ($request->filled('manager_id')) {
|
||||
$locations->where('locations.manager_id', '=', $request->input('manager_id'));
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
|
|
@ -33,7 +33,12 @@ class ReportsController extends Controller
|
|||
|
||||
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
|
||||
$actionlogs = $actionlogs->where('item_id', '=', $request->input('item_id'))
|
||||
->where('item_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')));
|
||||
->where('item_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')))
|
||||
->orWhere(function($query) use ($request)
|
||||
{
|
||||
$query->where('target_id', '=', $request->input('item_id'))
|
||||
->where('target_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')));
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('action_type')) {
|
||||
|
|
|
@ -148,7 +148,7 @@ class SettingsController extends Controller
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return Redirect
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function ajaxTestEmail()
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ class SettingsController extends Controller
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0.0]
|
||||
* @return Response
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function purgeBarcodes()
|
||||
{
|
||||
|
@ -211,7 +211,7 @@ class SettingsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0.0]
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
* @return array | JsonResponse
|
||||
*/
|
||||
public function showLoginAttempts(Request $request)
|
||||
{
|
||||
|
@ -229,6 +229,12 @@ class SettingsController extends Controller
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists backup files
|
||||
*
|
||||
* @author [A. Gianotto]
|
||||
* @return array | JsonResponse
|
||||
*/
|
||||
public function listBackups() {
|
||||
$settings = Setting::getSettings();
|
||||
$path = 'app/backups';
|
||||
|
@ -249,12 +255,12 @@ class SettingsController extends Controller
|
|||
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
|
||||
'modified_value' => $file_timestamp,
|
||||
'modified_display' => date($settings->date_display_format.' '.$settings->time_display_format, $file_timestamp),
|
||||
'backup_url' => config('app.url').'/settings/backups/download/'.basename($backup_files[$f]),
|
||||
|
||||
];
|
||||
$count++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,15 +270,56 @@ class SettingsController extends Controller
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Downloads a backup file.
|
||||
* We use response()->download() here instead of Storage::download() because Storage::download()
|
||||
* exhausts memory on larger files.
|
||||
*
|
||||
* @author [A. Gianotto]
|
||||
* @return JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function downloadBackup($file) {
|
||||
|
||||
$path = 'app/backups';
|
||||
if (Storage::exists($path.'/'.$file)) {
|
||||
$path = storage_path('app/backups');
|
||||
|
||||
if (Storage::exists('app/backups/'.$file)) {
|
||||
$headers = ['ContentType' => 'application/zip'];
|
||||
return Storage::download($path.'/'.$file, $file, $headers);
|
||||
return response()->download($path.'/'.$file, $file, $headers);
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')), 404);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines and downloads the latest backup
|
||||
*
|
||||
* @author [A. Gianotto]
|
||||
* @since [v6.3.1]
|
||||
* @return JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function downloadLatestBackup() {
|
||||
|
||||
$fileData = collect();
|
||||
foreach (Storage::files('app/backups') as $file) {
|
||||
if (pathinfo($file, PATHINFO_EXTENSION) == 'zip') {
|
||||
$fileData->push([
|
||||
'file' => $file,
|
||||
'date' => Storage::lastModified($file)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$newest = $fileData->sortByDesc('date')->first();
|
||||
if (Storage::exists($newest['file'])) {
|
||||
$headers = ['ContentType' => 'application/zip'];
|
||||
return response()->download(storage_path($newest['file']), basename($newest['file']), $headers);
|
||||
} else {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')), 404);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -274,11 +274,6 @@ class UsersController extends Controller
|
|||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
\Log::debug('Requested offset: '. $request->input('offset'));
|
||||
\Log::debug('App offset: '. app('api_offset_value'));
|
||||
\Log::debug('Actual offset: '. $offset);
|
||||
\Log::debug('Limit: '. $limit);
|
||||
|
||||
$total = $users->count();
|
||||
$users = $users->skip($offset)->take($limit)->get();
|
||||
|
||||
|
|
|
@ -148,30 +148,20 @@ class AssetMaintenancesController extends Controller
|
|||
*/
|
||||
public function edit($assetMaintenanceId = null)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the improvement management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif (! $assetMaintenance->asset) {
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', 'The asset associated with this maintenance does not exist.');
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
if ($assetMaintenance->completion_date == '0000-00-00') {
|
||||
$assetMaintenance->completion_date = null;
|
||||
}
|
||||
|
||||
if ($assetMaintenance->start_date == '0000-00-00') {
|
||||
$assetMaintenance->start_date = null;
|
||||
}
|
||||
|
||||
if ($assetMaintenance->cost == '0.00') {
|
||||
$assetMaintenance->cost = null;
|
||||
}
|
||||
|
||||
// Prepare Improvement Type List
|
||||
$assetMaintenanceType = [
|
||||
|
@ -203,8 +193,10 @@ class AssetMaintenancesController extends Controller
|
|||
// Check if the asset maintenance exists
|
||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
|
||||
// Redirect to the asset maintenance management page
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
|
|
@ -442,7 +442,6 @@ class AssetModelsController extends Controller
|
|||
$del_count = 0;
|
||||
|
||||
foreach ($models as $model) {
|
||||
\Log::debug($model->id);
|
||||
|
||||
if ($model->assets_count > 0) {
|
||||
$del_error_count++;
|
||||
|
@ -452,8 +451,6 @@ class AssetModelsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
\Log::debug($del_count);
|
||||
\Log::debug($del_error_count);
|
||||
|
||||
if ($del_error_count == 0) {
|
||||
return redirect()->route('models.index')
|
||||
|
|
|
@ -8,6 +8,7 @@ use App\Models\Location;
|
|||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Locations for
|
||||
|
@ -168,7 +169,7 @@ class LocationsController extends Controller
|
|||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found'));
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
if ($location->users()->count() > 0) {
|
||||
|
@ -238,7 +239,7 @@ class LocationsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $locationId
|
||||
* @since [v6.0.14]
|
||||
* @return View
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function getClone($locationId = null)
|
||||
{
|
||||
|
@ -272,8 +273,97 @@ class LocationsController extends Controller
|
|||
|
||||
}
|
||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a view that allows the user to bulk delete locations
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v6.3.1]
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function postBulkDelete(Request $request)
|
||||
{
|
||||
$locations_raw_array = $request->input('ids');
|
||||
|
||||
// Make sure some IDs have been selected
|
||||
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||
$locations = Location::whereIn('id', $locations_raw_array)
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')->get();
|
||||
|
||||
$valid_count = 0;
|
||||
foreach ($locations as $location) {
|
||||
if ($location->isDeletable()) {
|
||||
$valid_count++;
|
||||
}
|
||||
}
|
||||
return view('locations/bulk-delete', compact('locations'))->with('valid_count', $valid_count);
|
||||
}
|
||||
|
||||
return redirect()->route('models.index')
|
||||
->with('error', 'You must select at least one model to edit.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that locations can be deleted and deletes them if they can
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v6.3.1]
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postBulkDeleteStore(Request $request) {
|
||||
$locations_raw_array = $request->input('ids');
|
||||
|
||||
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||
$locations = Location::whereIn('id', $locations_raw_array)->get();
|
||||
|
||||
$success_count = 0;
|
||||
$error_count = 0;
|
||||
|
||||
foreach ($locations as $location) {
|
||||
|
||||
// Can we delete this location?
|
||||
if ($location->isDeletable()) {
|
||||
$location->delete();
|
||||
$success_count++;
|
||||
} else {
|
||||
$error_count++;
|
||||
}
|
||||
}
|
||||
|
||||
\Log::debug('Success count: '.$success_count);
|
||||
\Log::debug('Error count: '.$error_count);
|
||||
// Complete success
|
||||
if ($success_count == count($locations_raw_array)) {
|
||||
return redirect()
|
||||
->route('locations.index')
|
||||
->with('success', trans_choice('general.bulk.delete.success', $success_count,
|
||||
['object_type' => trans_choice('general.location_plural', $success_count), 'count' => $success_count]
|
||||
));
|
||||
}
|
||||
|
||||
// Partial success
|
||||
if ($error_count > 0) {
|
||||
return redirect()
|
||||
->route('locations.index')
|
||||
->with('warning', trans('general.bulk.partial_success',
|
||||
['success' => $success_count, 'error' => $error_count, 'object_type' => trans('general.locations')]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Nothing was selected - return to the index
|
||||
return redirect()
|
||||
->route('locations.index')
|
||||
->with('error', trans('general.bulk.nothing_selected',
|
||||
['object_type' => trans('general.locations')]
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -296,8 +296,8 @@ class ReportsController extends Controller
|
|||
e($actionlog->itemType()),
|
||||
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
|
||||
($actionlog->item) ? $actionlog->item->serial : null,
|
||||
($actionlog->item->model) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null,
|
||||
($actionlog->item->model) ? $actionlog->item->model->model_number : null,
|
||||
(($actionlog->item) && ($actionlog->item->model)) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null,
|
||||
(($actionlog->item) && ($actionlog->item->model)) ? $actionlog->item->model->model_number : null,
|
||||
$target_name,
|
||||
($actionlog->note) ? e($actionlog->note) : '',
|
||||
$actionlog->log_meta,
|
||||
|
|
|
@ -425,8 +425,6 @@ class SettingsController extends Controller
|
|||
if (! config('app.lock_passwords')) {
|
||||
$setting->site_name = $request->input('site_name');
|
||||
$setting->custom_css = $request->input('custom_css');
|
||||
}
|
||||
|
||||
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
|
||||
|
||||
if ('1' == $request->input('clear_logo')) {
|
||||
|
@ -446,8 +444,8 @@ class SettingsController extends Controller
|
|||
}
|
||||
|
||||
|
||||
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
|
||||
|
||||
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
|
||||
|
||||
if ('1' == $request->input('clear_label_logo')) {
|
||||
Storage::disk('public')->delete($setting->label_logo);
|
||||
|
@ -484,6 +482,7 @@ class SettingsController extends Controller
|
|||
|
||||
// If they are uploading an image, validate it and upload it
|
||||
}
|
||||
}
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
|
|
|
@ -97,7 +97,7 @@ class ImageUploadRequest extends Request
|
|||
|
||||
if (!config('app.lock_passwords')) {
|
||||
|
||||
$ext = $image->getClientOriginalExtension();
|
||||
$ext = $image->guessExtension();
|
||||
$file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
|
||||
|
||||
\Log::info('File name will be: '.$file_name);
|
||||
|
|
|
@ -28,12 +28,20 @@ class AssetMaintenancesTransformer
|
|||
'id' => (int) $assetmaintenance->asset->id,
|
||||
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
|
||||
'asset_tag'=> e($assetmaintenance->asset->asset_tag),
|
||||
|
||||
'serial'=> e($assetmaintenance->asset->serial),
|
||||
'deleted_at'=> e($assetmaintenance->asset->deleted_at),
|
||||
'created_at'=> e($assetmaintenance->asset->created_at),
|
||||
] : null,
|
||||
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->model->id,
|
||||
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
|
||||
] : null,
|
||||
'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_meta' => e($assetmaintenance->asset->present()->statusMeta),
|
||||
] : null,
|
||||
'company' => (($assetmaintenance->asset) && ($assetmaintenance->asset->company)) ? [
|
||||
'id' => (int) $assetmaintenance->asset->company->id,
|
||||
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
|
||||
|
@ -64,7 +72,7 @@ class AssetMaintenancesTransformer
|
|||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', Asset::class),
|
||||
'update' => (Gate::allows('update', Asset::class) && ($assetmaintenance->asset->deleted_at=='')) ? true : false,
|
||||
'delete' => Gate::allows('delete', Asset::class),
|
||||
];
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ class LocationsTransformer
|
|||
$permissions_array['available_actions'] = [
|
||||
'update' => Gate::allows('update', Location::class) ? true : false,
|
||||
'delete' => $location->isDeletable(),
|
||||
'bulk_selectable' => [
|
||||
'delete' => $location->isDeletable()
|
||||
],
|
||||
'clone' => (Gate::allows('create', Location::class) && ($location->deleted_at == '')),
|
||||
];
|
||||
|
||||
|
|
|
@ -46,10 +46,9 @@ class AccessoryImporter extends ItemImporter
|
|||
$this->item['min_amt'] = $this->findCsvMatch($row, "min_amt");
|
||||
$accessory->fill($this->sanitizeItemForStoring($accessory));
|
||||
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
// $accessory->unsetEventDispatcher();
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$accessory->setImported(true);
|
||||
if ($accessory->save()) {
|
||||
$accessory->logCreate('Imported using CSV Importer');
|
||||
$this->log('Accessory '.$this->item['name'].' was created');
|
||||
|
||||
return;
|
||||
|
|
|
@ -135,10 +135,10 @@ class AssetImporter extends ItemImporter
|
|||
$asset->{$custom_field} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$asset->setImported(true);
|
||||
if ($asset->save()) {
|
||||
|
||||
$asset->logCreate(trans('general.importer.import_note'));
|
||||
$this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||
|
||||
// If we have a target to checkout to, lets do so.
|
||||
|
|
|
@ -48,10 +48,10 @@ class ComponentImporter extends ItemImporter
|
|||
$this->log('No matching component, creating one');
|
||||
$component = new Component;
|
||||
$component->fill($this->sanitizeItemForStoring($component));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$component->unsetEventDispatcher();
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$component->setImported(true);
|
||||
if ($component->save()) {
|
||||
$component->logCreate('Imported using CSV Importer');
|
||||
$this->log('Component '.$this->item['name'].' was created');
|
||||
|
||||
// If we have an asset tag, checkout to that asset.
|
||||
|
|
|
@ -45,10 +45,10 @@ class ConsumableImporter extends ItemImporter
|
|||
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
||||
$this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt"));
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$consumable->unsetEventDispatcher();
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$consumable->setImported(true);
|
||||
if ($consumable->save()) {
|
||||
$consumable->logCreate('Imported using CSV Importer');
|
||||
$this->log('Consumable '.$this->item['name'].' was created');
|
||||
|
||||
return;
|
||||
|
|
|
@ -85,10 +85,10 @@ class LicenseImporter extends ItemImporter
|
|||
} else {
|
||||
$license->fill($this->sanitizeItemForStoring($license));
|
||||
}
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
// $license->unsetEventDispatcher();
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
$license->setImported(true);
|
||||
if ($license->save()) {
|
||||
$license->logCreate('Imported using csv importer');
|
||||
$this->log('License '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||
|
||||
// Lets try to checkout seats if the fields exist and we have seats.
|
||||
|
|
|
@ -69,9 +69,9 @@ class CheckoutableListener
|
|||
}
|
||||
}
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error("Exception caught during checkout notification: " . $e->getMessage());
|
||||
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ class CheckoutableListener
|
|||
);
|
||||
}
|
||||
//slack doesn't include the url in its messaging format so this is needed to hit the endpoint
|
||||
if(Setting::getSettings()->webhook_selected =='slack') {
|
||||
if(Setting::getSettings()->webhook_selected =='slack' || Setting::getSettings()->webhook_selected =='general') {
|
||||
|
||||
if ($this->shouldSendWebhookNotification()) {
|
||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||
|
@ -124,9 +124,9 @@ class CheckoutableListener
|
|||
}
|
||||
|
||||
} catch (ClientException $e) {
|
||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
||||
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error("Exception caught during checkin notification: " . $e->getMessage());
|
||||
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ class Actionlog extends SnipeModel
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
// This is to manually set the source (via setActionSource()) for determineActionSource()
|
||||
protected ?string $source = null;
|
||||
|
||||
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
||||
use SoftDeletes;
|
||||
use Presentable;
|
||||
|
@ -341,7 +344,12 @@ class Actionlog extends SnipeModel
|
|||
* @since v6.3.0
|
||||
* @return string
|
||||
*/
|
||||
public function determineActionSource() {
|
||||
public function determineActionSource(): string
|
||||
{
|
||||
// This is a manually set source
|
||||
if($this->source) {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
// This is an API call
|
||||
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
|
||||
|
@ -358,4 +366,10 @@ class Actionlog extends SnipeModel
|
|||
return 'cli/unknown';
|
||||
|
||||
}
|
||||
|
||||
// Manually sets $this->source for determineActionSource()
|
||||
public function setActionSource($source = null): void
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1560,7 +1560,7 @@ class Asset extends Depreciable
|
|||
*
|
||||
* In short, this set of statements tells the query builder to ONLY query against an
|
||||
* actual field that's being passed if it doesn't meet known relational fields. This
|
||||
* allows us to query custom fields directly in the assetsv table
|
||||
* allows us to query custom fields directly in the assets table
|
||||
* (regardless of their name) and *skip* any fields that we already know can only be
|
||||
* searched through relational searches that we do earlier in this method.
|
||||
*
|
||||
|
|
|
@ -62,7 +62,15 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['title', 'notes', 'asset_maintenance_type', 'cost', 'start_date', 'completion_date'];
|
||||
protected $searchableAttributes =
|
||||
[
|
||||
'title',
|
||||
'notes',
|
||||
'asset_maintenance_type',
|
||||
'cost',
|
||||
'start_date',
|
||||
'completion_date'
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
|
@ -70,9 +78,10 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'asset' => ['name', 'asset_tag'],
|
||||
'asset' => ['name', 'asset_tag', 'serial'],
|
||||
'asset.model' => ['name', 'model_number'],
|
||||
'asset.supplier' => ['name'],
|
||||
'asset.assetstatus' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
];
|
||||
|
||||
|
@ -197,6 +206,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
->orderBy('suppliers_maintenances.name', $order);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to order on admin user
|
||||
*
|
||||
|
@ -239,4 +249,33 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
|
||||
->orderBy('assets.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on serial
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param string $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderByAssetSerial($query, $order)
|
||||
{
|
||||
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
|
||||
->orderBy('assets.serial', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on status label name
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderStatusName($query, $order)
|
||||
{
|
||||
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
|
||||
->leftjoin('status_labels as maintained_asset_status', 'maintained_asset_status.id', '=', 'maintained_asset.status_id')
|
||||
->orderBy('maintained_asset_status.name', $order);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,10 @@ class Location extends SnipeModel
|
|||
|
||||
|
||||
/**
|
||||
* Determine whether or not this location can be deleted
|
||||
* Determine whether or not this location can be deleted.
|
||||
*
|
||||
* This method requires the eager loading of the relationships in order to determine whether
|
||||
* it can be deleted. It's tempting to load those here, but that increases the query load considerably.
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v3.0]
|
||||
|
@ -104,9 +107,10 @@ class Location extends SnipeModel
|
|||
public function isDeletable()
|
||||
{
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assignedAssets()->count() === 0)
|
||||
&& ($this->assets()->count() === 0)
|
||||
&& ($this->users()->count() === 0);
|
||||
&& ($this->assets_count === 0)
|
||||
&& ($this->assigned_assets_count === 0)
|
||||
&& ($this->children_count === 0)
|
||||
&& ($this->users_count === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,9 @@ use Illuminate\Support\Facades\Auth;
|
|||
|
||||
trait Loggable
|
||||
{
|
||||
// an attribute for setting whether or not the item was imported
|
||||
public ?bool $imported = false;
|
||||
|
||||
/**
|
||||
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
||||
* @since [v3.4]
|
||||
|
@ -18,6 +21,11 @@ trait Loggable
|
|||
return $this->morphMany(Actionlog::class, 'item');
|
||||
}
|
||||
|
||||
public function setImported(bool $bool): void
|
||||
{
|
||||
$this->imported = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
||||
* @since [v3.4]
|
||||
|
|
|
@ -63,34 +63,8 @@ class CheckinAccessoryNotification extends Notification
|
|||
if ($this->target instanceof User && $this->target->email != '') {
|
||||
\Log::debug('The target is a user');
|
||||
|
||||
/**
|
||||
* Send an email if the asset requires acceptance,
|
||||
* so the user can accept or decline the asset
|
||||
*/
|
||||
if (($this->item->requireAcceptance()) || ($this->item->getEula()) || ($this->item->checkin_email())) {
|
||||
$notifyBy[] = 'mail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email if the asset requires acceptance,
|
||||
* so the user can accept or decline the asset
|
||||
*/
|
||||
if ($this->item->requireAcceptance()) {
|
||||
\Log::debug('This accessory requires acceptance');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email if the item has a EULA, since the user should always receive it
|
||||
*/
|
||||
if ($this->item->getEula()) {
|
||||
\Log::debug('This accessory has a EULA');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email if an email should be sent at checkin/checkout
|
||||
*/
|
||||
if ($this->item->checkin_email()) {
|
||||
\Log::debug('This accessory has a checkin_email()');
|
||||
$notifyBy[] = 'mail';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ class AccessoryObserver
|
|||
$logAction->item_id = $accessory->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
if($accessory->imported) {
|
||||
$logAction->setActionSource('importer');
|
||||
}
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,9 @@ class AssetObserver
|
|||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
if($asset->imported) {
|
||||
$logAction->setActionSource('importer');
|
||||
}
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ class ComponentObserver
|
|||
$logAction->item_id = $component->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
if($component->imported) {
|
||||
$logAction->setActionSource('importer');
|
||||
}
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ class ConsumableObserver
|
|||
$logAction->item_id = $consumable->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
if($consumable->imported) {
|
||||
$logAction->setActionSource('importer');
|
||||
}
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ class LicenseObserver
|
|||
$logAction->item_id = $license->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->user_id = Auth::id();
|
||||
if($license->imported) {
|
||||
$logAction->setActionSource('importer');
|
||||
}
|
||||
$logAction->logaction('create');
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,19 @@ class AssetMaintenancesPresenter extends Presenter
|
|||
'sortable' => true,
|
||||
'title' => trans('admin/hardware/table.asset_tag'),
|
||||
'formatter' => 'assetTagLinkFormatter',
|
||||
], [
|
||||
'field' => 'serial',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/hardware/table.serial'),
|
||||
'formatter' => 'assetSerialLinkFormatter',
|
||||
], [
|
||||
'field' => 'status_label',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/hardware/table.status'),
|
||||
'visible' => true,
|
||||
'formatter' => 'statuslabelsLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'model',
|
||||
'searchable' => true,
|
||||
|
|
|
@ -14,7 +14,11 @@ class LocationPresenter extends Presenter
|
|||
public static function dataTableLayout()
|
||||
{
|
||||
$layout = [
|
||||
|
||||
[
|
||||
'field' => 'bulk_selectable',
|
||||
'checkbox' => true,
|
||||
'formatter' => 'checkboxEnabledFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
|
|
|
@ -39,24 +39,12 @@ class SettingsServiceProvider extends ServiceProvider
|
|||
$limit = abs($int_limit);
|
||||
}
|
||||
|
||||
// \Log::debug('Max in env: '.config('app.max_results'));
|
||||
// \Log::debug('Original requested limit: '.request('limit'));
|
||||
// \Log::debug('Int limit: '.$int_limit);
|
||||
// \Log::debug('Modified limit: '.$limit);
|
||||
// \Log::debug('------------------------------');
|
||||
|
||||
|
||||
return $limit;
|
||||
});
|
||||
|
||||
// Make sure the offset is actually set and is an integer
|
||||
\App::singleton('api_offset_value', function () {
|
||||
$offset = intval(request('offset'));
|
||||
// \Log::debug('Original requested offset: '.request('offset'));
|
||||
// \Log::debug('Modified offset: '.$offset);
|
||||
// \Log::debug('------------------------------');
|
||||
|
||||
|
||||
return $offset;
|
||||
});
|
||||
|
||||
|
|
|
@ -90,14 +90,10 @@ class Label implements View
|
|||
$assetData->put('id', $asset->id);
|
||||
$assetData->put('tag', $asset->asset_tag);
|
||||
|
||||
if ($template->getSupportTitle()) {
|
||||
|
||||
if ($asset->company && !empty($settings->label2_title)) {
|
||||
$title = str_replace('{COMPANY}', $asset->company->name, $settings->label2_title);
|
||||
$settings->qr_text;
|
||||
if ($template->getSupportTitle() && !empty($settings->label2_title)) {
|
||||
$title = str_replace('{COMPANY}', data_get($asset, 'company.name'), $settings->label2_title);
|
||||
$assetData->put('title', $title);
|
||||
}
|
||||
}
|
||||
|
||||
if ($template->getSupportLogo()) {
|
||||
|
||||
|
|
|
@ -77,7 +77,8 @@
|
|||
"watson/validating": "^6.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ldap": "*"
|
||||
"ext-ldap": "*",
|
||||
"ext-zip": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^6.6",
|
||||
|
|
44
composer.lock
generated
44
composer.lock
generated
|
@ -4804,16 +4804,16 @@
|
|||
},
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"version": "8.3.5",
|
||||
"version": "8.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git",
|
||||
"reference": "7aeb7c42b463b1a6fe4d084d3145e2fa22436876"
|
||||
"reference": "007dc5f6c0151a73b133fec36c9686cc956209d3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/7aeb7c42b463b1a6fe4d084d3145e2fa22436876",
|
||||
"reference": "7aeb7c42b463b1a6fe4d084d3145e2fa22436876",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/007dc5f6c0151a73b133fec36c9686cc956209d3",
|
||||
"reference": "007dc5f6c0151a73b133fec36c9686cc956209d3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4880,7 +4880,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth2-server/issues",
|
||||
"source": "https://github.com/thephpleague/oauth2-server/tree/8.3.5"
|
||||
"source": "https://github.com/thephpleague/oauth2-server/tree/8.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4888,7 +4888,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-03T21:21:28+00:00"
|
||||
"time": "2023-08-02T22:54:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/uri",
|
||||
|
@ -9669,16 +9669,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v5.4.10",
|
||||
"version": "v5.4.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "255ae3b0a488d78fbb34da23d3e0c059874b5948"
|
||||
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/255ae3b0a488d78fbb34da23d3e0c059874b5948",
|
||||
"reference": "255ae3b0a488d78fbb34da23d3e0c059874b5948",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
|
||||
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -9761,7 +9761,7 @@
|
|||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.4.10"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.4.20"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -9777,7 +9777,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-26T16:57:59+00:00"
|
||||
"time": "2023-02-01T08:18:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
|
@ -12647,16 +12647,16 @@
|
|||
},
|
||||
{
|
||||
"name": "composer/composer",
|
||||
"version": "2.6.6",
|
||||
"version": "2.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/composer.git",
|
||||
"reference": "683557bd2466072777309d039534bb1332d0dda5"
|
||||
"reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/composer/zipball/683557bd2466072777309d039534bb1332d0dda5",
|
||||
"reference": "683557bd2466072777309d039534bb1332d0dda5",
|
||||
"url": "https://api.github.com/repos/composer/composer/zipball/aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
|
||||
"reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -12674,7 +12674,7 @@
|
|||
"seld/jsonlint": "^1.4",
|
||||
"seld/phar-utils": "^1.2",
|
||||
"seld/signal-handler": "^2.0",
|
||||
"symfony/console": "^5.4.11 || ^6.0.11",
|
||||
"symfony/console": "^5.4.11 || ^6.0.11 || ^7",
|
||||
"symfony/filesystem": "^5.4 || ^6.0 || ^7",
|
||||
"symfony/finder": "^5.4 || ^6.0 || ^7",
|
||||
"symfony/polyfill-php73": "^1.24",
|
||||
|
@ -12688,7 +12688,7 @@
|
|||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1",
|
||||
"phpstan/phpstan-symfony": "^1.2.10",
|
||||
"symfony/phpunit-bridge": "^6.0 || ^7"
|
||||
"symfony/phpunit-bridge": "^6.4.1 || ^7.0.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
|
||||
|
@ -12701,7 +12701,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.6-dev"
|
||||
"dev-main": "2.7-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
|
@ -12741,7 +12741,7 @@
|
|||
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||
"issues": "https://github.com/composer/composer/issues",
|
||||
"security": "https://github.com/composer/composer/security/policy",
|
||||
"source": "https://github.com/composer/composer/tree/2.6.6"
|
||||
"source": "https://github.com/composer/composer/tree/2.7.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -12757,7 +12757,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-08T17:32:26+00:00"
|
||||
"time": "2024-02-09T14:26:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/metadata-minifier",
|
||||
|
@ -17088,5 +17088,5 @@
|
|||
"ext-pdo": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?php
|
||||
return array (
|
||||
'app_version' => 'v6.3.0',
|
||||
'full_app_version' => 'v6.3.0 - build 12490-g9136415bb',
|
||||
'build_version' => '12490',
|
||||
'app_version' => 'v6.3.1',
|
||||
'full_app_version' => 'v6.3.1 - build 12672-g00cea3eb3',
|
||||
'build_version' => '12672',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'g9136415bb',
|
||||
'full_hash' => 'v6.3.0-729-g9136415bb',
|
||||
'branch' => 'develop',
|
||||
'hash_version' => 'g00cea3eb3',
|
||||
'full_hash' => 'v6.3.1-180-g00cea3eb3',
|
||||
'branch' => 'master',
|
||||
);
|
|
@ -8,6 +8,7 @@ use App\Models\Location;
|
|||
use App\Models\Manufacturer;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class AccessoryFactory extends Factory
|
||||
|
@ -140,4 +141,16 @@ class AccessoryFactory extends Factory
|
|||
$accessory->category->update(['require_acceptance' => 1]);
|
||||
});
|
||||
}
|
||||
|
||||
public function checkedOutToUser(User $user = null)
|
||||
{
|
||||
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
93
package-lock.json
generated
93
package-lock.json
generated
|
@ -1565,9 +1565,9 @@
|
|||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "8.56.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.1.tgz",
|
||||
"integrity": "sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==",
|
||||
"version": "8.56.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
|
||||
"integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
|
||||
"requires": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
|
@ -2350,9 +2350,9 @@
|
|||
}
|
||||
},
|
||||
"alpinejs": {
|
||||
"version": "3.13.3",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
|
||||
"integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
|
||||
"version": "3.13.5",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.5.tgz",
|
||||
"integrity": "sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "~3.1.1"
|
||||
}
|
||||
|
@ -3611,6 +3611,7 @@
|
|||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
|
||||
"integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001286",
|
||||
"electron-to-chromium": "^1.4.17",
|
||||
|
@ -3731,7 +3732,8 @@
|
|||
"caniuse-lite": {
|
||||
"version": "1.0.30001292",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz",
|
||||
"integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw=="
|
||||
"integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==",
|
||||
"dev": true
|
||||
},
|
||||
"canvg": {
|
||||
"version": "3.0.10",
|
||||
|
@ -4931,7 +4933,8 @@
|
|||
"electron-to-chromium": {
|
||||
"version": "1.4.28",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz",
|
||||
"integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg=="
|
||||
"integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.4",
|
||||
|
@ -17418,7 +17421,8 @@
|
|||
"node-releases": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
|
||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
|
||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
|
@ -20097,18 +20101,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.89.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
|
||||
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
|
||||
"version": "5.90.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz",
|
||||
"integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==",
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@types/estree": "^1.0.5",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"browserslist": "^4.14.5",
|
||||
"browserslist": "^4.21.10",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.15.0",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
|
@ -20122,15 +20126,15 @@
|
|||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.7",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"watchpack": "^2.4.0",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA=="
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
|
@ -20138,9 +20142,9 @@
|
|||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.20",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
|
||||
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
|
||||
"version": "0.3.22",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
|
||||
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
|
@ -20151,16 +20155,42 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
|
||||
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001587",
|
||||
"electron-to-chromium": "^1.4.668",
|
||||
"node-releases": "^2.0.14",
|
||||
"update-browserslist-db": "^1.0.13"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001587",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz",
|
||||
"integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.670",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.670.tgz",
|
||||
"integrity": "sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A=="
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
|
@ -20172,17 +20202,17 @@
|
|||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
||||
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"requires": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.26.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz",
|
||||
"integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==",
|
||||
"version": "5.27.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
|
||||
"integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
|
@ -20202,6 +20232,15 @@
|
|||
"terser": "^5.26.0"
|
||||
}
|
||||
},
|
||||
"update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"webpack-sources": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
"ajv": "^6.12.6",
|
||||
"alpinejs": "^3.13.3",
|
||||
"alpinejs": "^3.13.5",
|
||||
"blueimp-file-upload": "^9.34.0",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
|
@ -59,6 +59,6 @@
|
|||
"tableexport.jquery.plugin": "1.28.0",
|
||||
"tether": "^1.4.0",
|
||||
"vue-resource": "^1.5.2",
|
||||
"webpack": "^5.89.0"
|
||||
"webpack": "^5.90.0"
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
BIN
public/css/dist/bootstrap-table.css
vendored
BIN
public/css/dist/bootstrap-table.css
vendored
Binary file not shown.
BIN
public/css/dist/signature-pad.min.css
vendored
BIN
public/css/dist/signature-pad.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.css
vendored
BIN
public/css/dist/skins/skin-black-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.css
vendored
BIN
public/css/dist/skins/skin-black.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.min.css
vendored
BIN
public/css/dist/skins/skin-black.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.css
vendored
BIN
public/css/dist/skins/skin-blue.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.min.css
vendored
BIN
public/css/dist/skins/skin-blue.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-contrast.css
vendored
BIN
public/css/dist/skins/skin-contrast.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.css
vendored
BIN
public/css/dist/skins/skin-green-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.css
vendored
BIN
public/css/dist/skins/skin-green.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.min.css
vendored
BIN
public/css/dist/skins/skin-green.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.css
vendored
BIN
public/css/dist/skins/skin-orange.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.min.css
vendored
BIN
public/css/dist/skins/skin-orange.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.css
vendored
BIN
public/css/dist/skins/skin-purple.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.min.css
vendored
BIN
public/css/dist/skins/skin-purple.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.css
vendored
BIN
public/css/dist/skins/skin-red-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.css
vendored
BIN
public/css/dist/skins/skin-red.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.min.css
vendored
BIN
public/css/dist/skins/skin-red.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow.css
vendored
BIN
public/css/dist/skins/skin-yellow.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow.min.css
vendored
BIN
public/css/dist/skins/skin-yellow.min.css
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/js/dist/all-defer.js
vendored
BIN
public/js/dist/all-defer.js
vendored
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
BIN
public/js/dist/bootstrap-table.js
vendored
BIN
public/js/dist/bootstrap-table.js
vendored
Binary file not shown.
|
@ -1,24 +1,24 @@
|
|||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=ea5f3edebafdb29b616d23fa89106080",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=9a69d65b9f2b35e6d17c6a72e76424d5",
|
||||
"/css/build/app.css": "/css/build/app.css?id=5a516232bc9f1488514577cf3c8108eb",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=dc383f8560a8d4adb51d44fb4043e03b",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=e6e53eef152bba01a4c666a4d8b01117",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=07273f6ca3c698a39e8fc2075af4fa07",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=c1f33574ecb9d3e69d9b8fe5bd68e101",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=7d92dea45d94be7e1d4e427c728d335d",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=8ca888bbc050d9680cbb65021382acba",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=b061bb141af3bdb6280c6ee772cf8f4f",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=d419cb63a12dc175d71645c876bfc2ab",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=ebd6663d2f61487038c9947111be2c73",
|
||||
"/js/build/app.js": "/js/build/app.js?id=cad71122a8a3f0cd6c5960e95f4f0f8a",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=6a7f37afafaaf9ccea99a7391cdf02b2",
|
||||
"/css/build/app.css": "/css/build/app.css?id=ea3875faceb1d09c162d00fbf5b4df57",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=f25c77ed07053646a42e9c19923d24fa",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=9f944e8021781af1ce45d27765d1c0c2",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=6cf460bed48ab738041f60231a3f005a",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=2ef1965d45a0a72336dd8e9b93f82d80",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=69e5d8e4e818f05fd882cceb758d1eba",
|
||||
|
@ -32,21 +32,21 @@
|
|||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=afa255bf30b2a7c11a97e3165128d183",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=a2b971da417306a63385c8098acfe4af",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=29340c70d13855fa0165cd4d799c6f5b",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=13bdb521e0c745d7f81dae3fb110b650",
|
||||
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=7f9a130eda6916eaa32a0a57e81918f3",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=d419cb63a12dc175d71645c876bfc2ab",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=b061bb141af3bdb6280c6ee772cf8f4f",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=07273f6ca3c698a39e8fc2075af4fa07",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=c1f33574ecb9d3e69d9b8fe5bd68e101",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=8ca888bbc050d9680cbb65021382acba",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=6fe68325d5356197672c27bc77cedcb4",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=7d92dea45d94be7e1d4e427c728d335d",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=e6e53eef152bba01a4c666a4d8b01117",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da"
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=a9bde3f908f73634c0d1b4cd3f835092",
|
||||
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=19ccc62a8f1ea103dede4808837384d4",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=0ed42b67f9b02a74815e885bfd9e3f66",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=6cf460bed48ab738041f60231a3f005a",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=9f944e8021781af1ce45d27765d1c0c2",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=d409d9b1a3b69247df8b98941ba06e33",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460"
|
||||
}
|
||||
|
|
|
@ -182,6 +182,7 @@ return [
|
|||
'lock_passwords' => 'This field value will not be saved in a demo installation.',
|
||||
'feature_disabled' => 'This feature has been disabled for the demo installation.',
|
||||
'location' => 'Location',
|
||||
'location_plural' => 'Location|Locations',
|
||||
'locations' => 'Locations',
|
||||
'logo_size' => 'Square logos look best with Logo + Text. Logo maximum display size is 50px high x 500px wide. ',
|
||||
'logout' => 'Logout',
|
||||
|
@ -443,7 +444,6 @@ return [
|
|||
'sample_value' => 'Sample Value',
|
||||
'no_headers' => 'No Columns Found',
|
||||
'error_in_import_file' => 'There was an error reading the CSV file: :error',
|
||||
'percent_complete' => ':percent % Complete',
|
||||
'errors_importing' => 'Some Errors occurred while importing: ',
|
||||
'warning' => 'WARNING: :warning',
|
||||
'success_redirecting' => '"Success... Redirecting.',
|
||||
|
@ -459,6 +459,7 @@ return [
|
|||
'no_autoassign_licenses_help' => 'Do not include user for bulk-assigning through the license UI or cli tools.',
|
||||
'modal_confirm_generic' => 'Are you sure?',
|
||||
'cannot_be_deleted' => 'This item cannot be deleted',
|
||||
'cannot_be_edited' => 'This item cannot be edited.',
|
||||
'undeployable_tooltip' => 'This item cannot be checked out. Check the quantity remaining.',
|
||||
'serial_number' => 'Serial Number',
|
||||
'item_notes' => ':item Notes',
|
||||
|
@ -501,5 +502,17 @@ return [
|
|||
'action_source' => 'Action Source',
|
||||
'or' => 'or',
|
||||
'url' => 'URL',
|
||||
'edit_fieldset' => 'Edit fieldset fields and options',
|
||||
'bulk' => [
|
||||
'delete' =>
|
||||
[
|
||||
'header' => 'Bulk Delete :object_type',
|
||||
'warn' => 'You are about to delete one :object_type|You are about to delete :count :object_type',
|
||||
'success' => ':object_type successfully deleted|Successfully deleted :count :object_type',
|
||||
'error' => 'Could not delete :object_type',
|
||||
'nothing_selected' => 'No :object_type selected - nothing to do',
|
||||
'partial' => 'Deleted :success_count :object_type, but :error_count :object_type could not be deleted',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
@ -279,7 +279,7 @@
|
|||
</strong>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{!! nl2br(e($component->notes)) !!}
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($component->notes)) !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
@can('update', $custom_fieldset)
|
||||
<form method="post" action="{{ route('fields.disassociate', [$field, $custom_fieldset->id]) }}">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-sm btn-danger">{{ trans('button.remove') }}</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa fa-trash icon-white" aria-hidden="true"></i></button>
|
||||
</form>
|
||||
@endcan
|
||||
</td>
|
||||
|
@ -90,35 +90,34 @@
|
|||
<td colspan="8">
|
||||
{{ Form::open(['route' =>
|
||||
["fieldsets.associate",$custom_fieldset->id],
|
||||
'class'=>'form-horizontal',
|
||||
'class'=>'form-inline',
|
||||
'id' => 'ordering']) }}
|
||||
|
||||
|
||||
<div class="form-group col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="field_id" class="sr-only">
|
||||
{{ trans('admin/custom-field/general.add_field_to_fieldset')}}
|
||||
</label>
|
||||
{{ Form::select("field_id",$custom_fields_list,"",['aria-label'=>'field_id', 'class'=>'select2']) }}
|
||||
{{ Form::select("field_id",$custom_fields_list,"",['aria-label'=>'field_id', 'class'=>'select2', 'style' => 'min-width:400px;']) }}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-2" style="vertical-align: middle;">
|
||||
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('required', 'on', old('required'), array('aria-label'=>'required')) }}
|
||||
{{ trans('admin/custom_fields/general.required') }}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-2" style="display: none;">
|
||||
|
||||
{{ Form::text('order', $maxid, array('class' => 'form-control col-sm-1 col-md-1', 'style'=> 'width: 80px; padding-;right: 10px;', 'aria-label'=>'order', 'maxlength'=>'3', 'size'=>'3')) }}
|
||||
<div class="form-group" style="display: none;">
|
||||
{{ Form::text('order', $maxid, array('aria-label'=>'order', 'maxlength'=>'3', 'size'=>'3')) }}
|
||||
<label for="order">{{ trans('admin/custom_fields/general.order') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
||||
<button type="submit" class="btn btn-primary"> {{ trans('general.save') }}</button>
|
||||
<div class="checkbox-inline">
|
||||
<label>
|
||||
{{ Form::checkbox('required', 'on', old('required')) }}
|
||||
<span style="padding-left: 10px;">{{ trans('admin/custom_fields/general.required') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<span style="padding-left: 10px;">
|
||||
<button type="submit" class="btn btn-primary"> {{ trans('general.save') }}</button>
|
||||
</span>
|
||||
|
||||
{{ Form::close() }}
|
||||
|
||||
</td>
|
||||
|
|
|
@ -73,7 +73,14 @@
|
|||
<nobr>
|
||||
|
||||
@can('update', $fieldset)
|
||||
<a href="{{ route('fieldsets.edit', $fieldset->id) }}" class="btn btn-warning btn-sm">
|
||||
|
||||
<a href="{{ route('fieldsets.show', ['fieldset' => $fieldset->id]) }}" data-tooltip="true" title="{{ trans('general.edit_fieldset') }}">
|
||||
<button type="submit" class="btn btn-info btn-sm">
|
||||
<i class="fa-regular fa-rectangle-list"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('fieldsets.edit', $fieldset->id) }}" class="btn btn-warning btn-sm" data-tooltip="true" title="{{ trans('general.update') }}">
|
||||
<i class="fas fa-pencil-alt" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('button.edit') }}</span>
|
||||
</a>
|
||||
|
@ -82,9 +89,9 @@
|
|||
@can('delete', $fieldset)
|
||||
{{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete','style' => 'display:inline-block']) }}
|
||||
@if($fieldset->models->count() > 0)
|
||||
<button type="submit" class="btn btn-danger btn-sm disabled" disabled><i class="fas fa-trash"></i></button>
|
||||
<button type="submit" class="btn btn-danger btn-sm disabled" data-tooltip="true" title="{{ trans('general.cannot_be_deleted') }}" disabled><i class="fas fa-trash"></i></button>
|
||||
@else
|
||||
<button type="submit" class="btn btn-danger btn-sm"><i class="fas fa-trash"></i></button>
|
||||
<button type="submit" class="btn btn-danger btn-sm" data-tooltip="true" title="{{ trans('general.delete') }}"><i class="fas fa-trash"></i></button>
|
||||
@endif
|
||||
{{ Form::close() }}
|
||||
@endcan
|
||||
|
@ -188,7 +195,7 @@
|
|||
<nobr>
|
||||
{{ Form::open(array('route' => array('fields.destroy', $field->id), 'method' => 'delete', 'style' => 'display:inline-block')) }}
|
||||
@can('update', $field)
|
||||
<a href="{{ route('fields.edit', $field->id) }}" class="btn btn-warning btn-sm">
|
||||
<a href="{{ route('fields.edit', $field->id) }}" class="btn btn-warning btn-sm" data-tooltip="true" title="{{ trans('general.update') }}">
|
||||
<i class="fas fa-pencil-alt" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('button.edit') }}</span>
|
||||
</a>
|
||||
|
@ -197,11 +204,11 @@
|
|||
@can('delete', $field)
|
||||
|
||||
@if($field->fieldset->count()>0)
|
||||
<button type="submit" class="btn btn-danger btn-sm disabled" disabled>
|
||||
<button type="submit" class="btn btn-danger btn-sm disabled" data-tooltip="true" title="{{ trans('general.cannot_be_deleted') }}" disabled>
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('button.delete') }}</span></button>
|
||||
@else
|
||||
<button type="submit" class="btn btn-danger btn-sm">
|
||||
<button type="submit" class="btn btn-danger btn-sm" data-tooltip="true" title="{{ trans('general.delete') }}">
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('button.delete') }}</span>
|
||||
</button>
|
||||
|
|
|
@ -872,11 +872,13 @@
|
|||
|
||||
|
||||
@can('update', $asset)
|
||||
@if ($asset->deleted_at=='')
|
||||
<div class="col-md-12" style="padding-top: 5px;">
|
||||
<a href="{{ route('hardware.edit', $asset->id) }}" style="width: 100%;" class="btn btn-sm btn-primary hidden-print">
|
||||
{{ trans('admin/hardware/general.edit') }}
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
@endcan
|
||||
|
||||
@can('create', $asset)
|
||||
|
|
|
@ -140,17 +140,15 @@
|
|||
<div class="navbar-custom-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
@can('index', \App\Models\Asset::class)
|
||||
<li aria-hidden="true"
|
||||
{!! (Request::is('hardware*') ? ' class="active"' : '') !!} tabindex="-1">
|
||||
<li aria-hidden="true"{!! (Request::is('hardware*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ url('hardware') }}" accesskey="1" tabindex="-1">
|
||||
<i class="fas fa-barcode fa-fw" aria-hidden="true"></i>
|
||||
<i class="fas fa-barcode fa-fw"></i>
|
||||
<span class="sr-only">{{ trans('general.assets') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@can('view', \App\Models\License::class)
|
||||
<li aria-hidden="true"
|
||||
{!! (Request::is('licenses*') ? ' class="active"' : '') !!} tabindex="-1">
|
||||
<li aria-hidden="true"{!! (Request::is('licenses*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('licenses.index') }}" accesskey="2" tabindex="-1">
|
||||
<i class="far fa-save fa-fw"></i>
|
||||
<span class="sr-only">{{ trans('general.licenses') }}</span>
|
||||
|
@ -158,8 +156,7 @@
|
|||
</li>
|
||||
@endcan
|
||||
@can('index', \App\Models\Accessory::class)
|
||||
<li aria-hidden="true"
|
||||
{!! (Request::is('accessories*') ? ' class="active"' : '') !!} tabindex="-1">
|
||||
<li aria-hidden="true"{!! (Request::is('accessories*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('accessories.index') }}" accesskey="3" tabindex="-1">
|
||||
<i class="far fa-keyboard fa-fw"></i>
|
||||
<span class="sr-only">{{ trans('general.accessories') }}</span>
|
||||
|
@ -233,7 +230,8 @@
|
|||
<li {!! (Request::is('accessories/create') ? 'class="active"' : '') !!}>
|
||||
<a href="{{ route('accessories.create') }}" tabindex="-1">
|
||||
<i class="far fa-keyboard fa-fw" aria-hidden="true"></i>
|
||||
{{ trans('general.accessory') }}</a>
|
||||
{{ trans('general.accessory') }}
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@can('create', \App\Models\Consumable::class)
|
||||
|
|
70
resources/views/locations/bulk-delete.blade.php
Normal file
70
resources/views/locations/bulk-delete.blade.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
@extends('layouts/default')
|
||||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
{{ trans('general.bulk.delete.header', ['object_type' => trans_choice('general.location_plural', $valid_count)]) }}
|
||||
@parent
|
||||
@stop
|
||||
|
||||
@section('header_right')
|
||||
<a href="{{ URL::previous() }}" class="btn btn-primary pull-right">
|
||||
{{ trans('general.back') }}</a>
|
||||
@stop
|
||||
|
||||
{{-- Page content --}}
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<!-- left column -->
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<form class="form-horizontal" method="post" action="{{ route('locations.bulkdelete.store') }}" autocomplete="off" role="form">
|
||||
{{csrf_field()}}
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h2 class="box-title" style="color: red">{{ trans_choice('general.bulk.delete.warn', $valid_count, ['count' => $valid_count,'object_type' => trans_choice('general.location_plural', $valid_count)]) }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="box-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="col-md-1">
|
||||
<label>
|
||||
<input type="checkbox" id="checkAll" checked="checked">
|
||||
</label>
|
||||
</td>
|
||||
<td class="col-md-10">{{ trans('general.name') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($locations as $location)
|
||||
<tr{!! (($location->assets_count > 0 ) ? ' class="danger"' : '') !!}>
|
||||
<td>
|
||||
<input type="checkbox" name="ids[]" class="{ ($location->isDeletable() ? '' : ' disabled') }}" value="{{ $location->id }}" {!! (($location->isDeletable()) ? ' checked="checked"' : ' disabled') !!}>
|
||||
</td>
|
||||
<td>{{ $location->name }}</td>
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!-- /.box-body -->
|
||||
|
||||
<div class="box-footer text-right">
|
||||
<a class="btn btn-link pull-left" href="{{ URL::previous() }}">{{ trans('button.cancel') }}</a>
|
||||
<button type="submit" class="btn btn-success" id="submit-button"><i class="fas fa-check icon-white" aria-hidden="true"></i> {{ trans('general.delete') }}</button>
|
||||
</div><!-- /.box-footer -->
|
||||
</div><!-- /.box -->
|
||||
</form>
|
||||
</div> <!-- .col-md-12-->
|
||||
</div><!--.row-->
|
||||
@stop
|
||||
@section('moar_scripts')
|
||||
<script>
|
||||
|
||||
|
||||
$("#checkAll").change(function () {
|
||||
$("input:checkbox").prop('checked', $(this).prop("checked"));
|
||||
});
|
||||
|
||||
</script>
|
||||
@stop
|
|
@ -20,11 +20,17 @@
|
|||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
|
||||
@include('partials.locations-bulk-actions')
|
||||
|
||||
<table
|
||||
data-columns="{{ \App\Presenters\LocationPresenter::dataTableLayout() }}"
|
||||
data-cookie-id-table="locationTable"
|
||||
data-click-to-select="true"
|
||||
data-pagination="true"
|
||||
data-id-table="locationTable"
|
||||
data-toolbar="#locationsBulkEditToolbar"
|
||||
data-bulk-button-id="#bulkLocationsEditButton"
|
||||
data-bulk-form-id="#locationsBulkForm"
|
||||
data-search="true"
|
||||
data-show-footer="true"
|
||||
data-side-pagination="server"
|
||||
|
|
|
@ -309,7 +309,7 @@
|
|||
@if ($model->notes)
|
||||
<li>
|
||||
{{ trans('general.notes') }}:
|
||||
{{ $model->notes }}
|
||||
{!! nl2br(Helper::parseEscapedMarkedownInline($model->notes)) !!}
|
||||
</li>
|
||||
@endif
|
||||
|
||||
|
|
|
@ -139,12 +139,12 @@
|
|||
});
|
||||
|
||||
|
||||
// Handle whether or not the edit button should be disabled
|
||||
// Handle whether the edit button should be disabled
|
||||
$('.snipe-table').on('uncheck.bs.table', function () {
|
||||
|
||||
var buttonName = $(this).data('bulk-button-id');
|
||||
|
||||
if ($(this).bootstrapTable('getSelections').length == 0) {
|
||||
|
||||
$(buttonName).attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
|
@ -296,6 +296,10 @@
|
|||
|
||||
if ((row.available_actions) && (row.available_actions.update === true)) {
|
||||
actions += '<a href="{{ config('app.url') }}/' + dest + '/' + row.id + '/edit" class="actions btn btn-sm btn-warning" data-tooltip="true" title="{{ trans('general.update') }}"><i class="fas fa-pencil-alt" aria-hidden="true"></i><span class="sr-only">{{ trans('general.update') }}</span></a> ';
|
||||
} else {
|
||||
if ((row.available_actions) && (row.available_actions.update != true)) {
|
||||
actions += '<span data-tooltip="true" title="{{ trans('general.cannot_be_edited') }}"><a class="btn btn-warning btn-sm disabled" onClick="return false;"><i class="fas fa-pencil-alt"></i></a></span> ';
|
||||
}
|
||||
}
|
||||
|
||||
if ((row.available_actions) && (row.available_actions.delete === true)) {
|
||||
|
@ -394,17 +398,35 @@
|
|||
// Convert line breaks to <br>
|
||||
function notesFormatter(value) {
|
||||
if (value) {
|
||||
return value.replace(/(?:\r\n|\r|\n)/g, '<br />');;
|
||||
return value.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if checkbox should be selectable
|
||||
// Selectability is determined by the API field "selectable" which is set at the Presenter/API Transformer
|
||||
// However since different bulk actions have different requirements, we have to walk through the available_actions object
|
||||
// to determine whether to disable it
|
||||
function checkboxEnabledFormatter (value, row) {
|
||||
|
||||
// add some stuff to get the value of the select2 option here?
|
||||
|
||||
if ((row.available_actions) && (row.available_actions.bulk_selectable) && (row.available_actions.bulk_selectable.delete !== true)) {
|
||||
console.log('value for ID ' + row.id + ' is NOT true:' + row.available_actions.bulk_selectable.delete);
|
||||
return {
|
||||
disabled:true,
|
||||
//checked: false, <-- not sure this will work the way we want?
|
||||
}
|
||||
}
|
||||
console.log('value for ID ' + row.id + ' IS true:' + row.available_actions.bulk_selectable.delete);
|
||||
}
|
||||
|
||||
|
||||
// We need a special formatter for license seats, since they don't work exactly the same
|
||||
// Checkouts need the license ID, checkins need the specific seat ID
|
||||
|
||||
function licenseSeatInOutFormatter(value, row) {
|
||||
// The user is allowed to check the license seat out and it's available
|
||||
if ((row.available_actions.checkout == true) && (row.user_can_checkout == true) && ((!row.asset_id) && (!row.assigned_to))) {
|
||||
if ((row.available_actions.checkout === true) && (row.user_can_checkout === true) && ((!row.asset_id) && (!row.assigned_to))) {
|
||||
return '<a href="{{ config('app.url') }}/licenses/' + row.license_id + '/checkout/'+row.id+'" class="btn btn-sm bg-maroon" data-tooltip="true" title="{{ trans('general.checkout_tooltip') }}">{{ trans('general.checkout') }}</a>';
|
||||
} else {
|
||||
return '<a href="{{ config('app.url') }}/licenses/' + row.id + '/checkin" class="btn btn-sm bg-purple" data-tooltip="true" title="Check in this license seat.">{{ trans('general.checkin') }}</a>';
|
||||
|
@ -623,6 +645,9 @@
|
|||
|
||||
function assetTagLinkFormatter(value, row) {
|
||||
if ((row.asset) && (row.asset.id)) {
|
||||
if (row.asset.deleted_at!='') {
|
||||
return '<span style="white-space: nowrap;"><i class="fas fa-times text-danger"></i><span class="sr-only">deleted</span> <del><a href="{{ config('app.url') }}/hardware/' + row.asset.id + '" data-tooltip="true" title="{{ trans('admin/hardware/general.deleted') }}">' + row.asset.asset_tag + '</a></del></span>';
|
||||
}
|
||||
return '<a href="{{ config('app.url') }}/hardware/' + row.asset.id + '">' + row.asset.asset_tag + '</a>';
|
||||
}
|
||||
return '';
|
||||
|
@ -640,7 +665,17 @@
|
|||
if ((row.asset) && (row.asset.name)) {
|
||||
return '<a href="{{ config('app.url') }}/hardware/' + row.asset.id + '">' + row.asset.name + '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
function assetSerialLinkFormatter(value, row) {
|
||||
|
||||
if ((row.asset) && (row.asset.serial)) {
|
||||
if (row.asset.deleted_at!='') {
|
||||
return '<span style="white-space: nowrap;"><i class="fas fa-times text-danger"></i><span class="sr-only">deleted</span> <del><a href="{{ config('app.url') }}/hardware/' + row.asset.id + '" data-tooltip="true" title="{{ trans('admin/hardware/general.deleted') }}">' + row.asset.serial + '</a></del></span>';
|
||||
}
|
||||
return '<a href="{{ config('app.url') }}/hardware/' + row.asset.id + '">' + row.asset.serial + '</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function trueFalseFormatter(value) {
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
</label>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<label class="btn btn-default">
|
||||
<label class="btn btn-default{{ (config('app.lock_passwords')) ? ' disabled' : '' }}">
|
||||
{{ trans('button.select_file') }}
|
||||
<input type="file" name="{{ $logoVariable }}" class="js-uploadFile" id="{{ $logoId }}" accept="image/gif,image/jpeg,image/webp,image/png,image/svg,image/svg+xml" data-maxsize="{{ $maxSize ?? Helper::file_upload_max_size() }}"
|
||||
style="display:none; max-width: 90%">
|
||||
<input type="file" name="{{ $logoVariable }}" class="js-uploadFile" id="{{ $logoId }}" accept="{{ (isset($allowedTypes) ? $allowedTypes : "image/gif,image/jpeg,image/webp,image/png,image/svg,image/svg+xml") }}" data-maxsize="{{ $maxSize ?? Helper::file_upload_max_size() }}"
|
||||
style="display:none; max-width: 90%"{{ (config('app.lock_passwords')) ? ' disabled' : '' }}>
|
||||
</label>
|
||||
|
||||
<span class='label label-default' id="{{ $logoId }}-info"></span>
|
||||
|
|
20
resources/views/partials/locations-bulk-actions.blade.php
Normal file
20
resources/views/partials/locations-bulk-actions.blade.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
@can('delete', \App\Models\Location::class)
|
||||
<div id="locationsBulkEditToolbar">
|
||||
{{ Form::open([
|
||||
'method' => 'POST',
|
||||
'route' => ['locations.bulkdelete.show'],
|
||||
'class' => 'form-inline',
|
||||
'id' => 'locationsBulkForm']) }}
|
||||
|
||||
<div id="locations-toolbar">
|
||||
<label for="bulk_actions" class="sr-only">{{ trans('general.bulk_actions') }}</label>
|
||||
<select name="bulk_actions" class="form-control select2" style="width: 200px;" aria-label="bulk_actions">
|
||||
<option value="delete">{{ trans('general.bulk_delete') }}</option>
|
||||
</select>
|
||||
<button class="btn btn-primary" id="bulkLocationsEditButton" disabled>{{ trans('button.go') }}</button>
|
||||
</div>
|
||||
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
@endcan
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
'id' => 'modelsBulkForm']) }}
|
||||
|
||||
@if (request('status')!='deleted')
|
||||
@can('delete', \App\Models\User::class)
|
||||
@can('delete', \App\Models\AssetModel::class)
|
||||
<div id="models-toolbar">
|
||||
<label for="bulk_actions" class="sr-only">{{ trans('general.bulk_actions') }}</label>
|
||||
<select name="bulk_actions" class="form-control select2" style="width: 200px;" aria-label="bulk_actions">
|
||||
|
|
|
@ -1015,23 +1015,36 @@
|
|||
</div><!-- /.tab-pane -->
|
||||
|
||||
<div class="tab-pane" id="managed">
|
||||
<div class="table-responsive">
|
||||
<table class="table display table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-8">{{ trans('general.name') }}</th>
|
||||
<th class="col-md-4">{{ trans('general.date') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($user->managedLocations as $location)
|
||||
<tr>
|
||||
<td>{!! $location->present()->nameUrl() !!}</td>
|
||||
<td>{{ $location->created_at }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
||||
@include('partials.locations-bulk-actions')
|
||||
|
||||
|
||||
<table
|
||||
data-columns="{{ \App\Presenters\LocationPresenter::dataTableLayout() }}"
|
||||
data-cookie-id-table="locationTable"
|
||||
data-click-to-select="true"
|
||||
data-pagination="true"
|
||||
data-id-table="locationTable"
|
||||
data-toolbar="#locationsBulkEditToolbar"
|
||||
data-bulk-button-id="#bulkLocationsEditButton"
|
||||
data-bulk-form-id="#locationsBulkForm"
|
||||
data-search="true"
|
||||
data-show-footer="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
id="locationTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.locations.index', ['manager_id' => $user->id]) }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-locations-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div><!-- /consumables-tab -->
|
||||
</div><!-- /.tab-content -->
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue