Compare commits
1 Commits
v8.1.17
...
move_faker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c125eb8e6e |
@@ -4162,33 +4162,6 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Tinyblargon",
|
||||
"name": "Okean",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/76069640?v=4",
|
||||
"profile": "https://github.com/Tinyblargon",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "amedranogil",
|
||||
"name": "Alejandro Medrano",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6515064?v=4",
|
||||
"profile": "https://www.lst.tfo.upm.es/alejandro-medrano/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lukaskraic",
|
||||
"name": "Lukas Kraic",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
|
||||
"profile": "https://github.com/lukaskraic",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FixUpAssignedTypeWithoutAssignedTo extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:assigned-type-fixup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
|
||||
$this->info("Assets with an assigned_type but no assigned_to are fixed");
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -66,8 +66,8 @@ class PaveIt extends Command
|
||||
foreach ($custom_fields as $custom_field) {
|
||||
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
|
||||
|
||||
if (Schema::hasColumn('assets', $custom_field->db_column)) {
|
||||
Schema::table('assets', function ($table) use ($custom_field) {
|
||||
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
|
||||
\Schema::table('assets', function ($table) use ($custom_field) {
|
||||
$table->dropColumn($custom_field->db_column);
|
||||
});
|
||||
}
|
||||
@@ -84,8 +84,8 @@ class PaveIt extends Command
|
||||
}
|
||||
|
||||
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
|
||||
DB::statement('delete from oauth_clients WHERE id > 2');
|
||||
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
|
||||
\DB::statement('delete from oauth_clients WHERE id > 2');
|
||||
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
|
||||
|
||||
}
|
||||
}
|
||||
@@ -99,11 +99,8 @@ class SendAcceptanceReminder extends Command
|
||||
foreach ($no_email_list as $user) {
|
||||
$rows[] = [$user['id'], $user['name']];
|
||||
}
|
||||
|
||||
if (!empty($rows)) {
|
||||
$this->info("The following users do not have an email address:");
|
||||
$this->table($headers, $rows);
|
||||
}
|
||||
$this->info("The following users do not have an email address:");
|
||||
$this->table($headers, $rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class CheckoutableCheckedIn
|
||||
$this->checkedOutTo = $checkedOutTo;
|
||||
$this->checkedInBy = $checkedInBy;
|
||||
$this->note = $note;
|
||||
$this->action_date = $action_date ?? date('Y-m-d H:i:s');
|
||||
$this->action_date = $action_date ?? date('Y-m-d');
|
||||
$this->originalValues = $originalValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
use JsonException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@@ -108,25 +107,18 @@ class Handler extends ExceptionHandler
|
||||
|
||||
$statusCode = $e->getStatusCode();
|
||||
|
||||
// API throttle requests are handled in the RouteServiceProvider configureRateLimiting() method, so we don't need to handle them here
|
||||
switch ($e->getStatusCode()) {
|
||||
case '404':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
|
||||
case '429':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
|
||||
case '405':
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
|
||||
default:
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This handles API validation exceptions that happen at the Form Request level, so they
|
||||
// never even get to the controller where we normally nicely format JSON responses
|
||||
if ($e instanceof ValidationException) {
|
||||
$response = $this->invalidJson($request, $e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -151,10 +143,6 @@ class Handler extends ExceptionHandler
|
||||
$route = 'maintenances.index';
|
||||
} elseif ($route === 'licenseseats.index') {
|
||||
$route = 'licenses.index';
|
||||
} elseif ($route === 'customfields.index') {
|
||||
$route = 'fields.index';
|
||||
} elseif ($route === 'customfieldsets.index') {
|
||||
$route = 'fields.index';
|
||||
}
|
||||
|
||||
return redirect()
|
||||
@@ -213,7 +201,6 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
|
||||
@@ -1313,24 +1313,25 @@ class Helper
|
||||
switch ($item) {
|
||||
case 'asset':
|
||||
return 'fas fa-barcode';
|
||||
break;
|
||||
case 'accessory':
|
||||
return 'fas fa-keyboard';
|
||||
break;
|
||||
case 'component':
|
||||
return 'fas fa-hdd';
|
||||
break;
|
||||
case 'consumable':
|
||||
return 'fas fa-tint';
|
||||
break;
|
||||
case 'license':
|
||||
return 'far fa-save';
|
||||
break;
|
||||
case 'location':
|
||||
return 'fas fa-map-marker-alt';
|
||||
break;
|
||||
case 'user':
|
||||
return 'fas fa-user';
|
||||
case 'supplier':
|
||||
return 'fa-solid fa-store';
|
||||
case 'manufacturer':
|
||||
return 'fa-solid fa-building';
|
||||
case 'category':
|
||||
return 'fa-solid fa-table-columns';
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ class StorageHelper
|
||||
$disk = config('filesystems.default');
|
||||
}
|
||||
switch (config("filesystems.disks.$disk.driver")) {
|
||||
case 'local':
|
||||
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
|
||||
case 'local':
|
||||
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
|
||||
|
||||
case 's3':
|
||||
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
|
||||
case 's3':
|
||||
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
|
||||
|
||||
default:
|
||||
return Storage::disk($disk)->download($filename);
|
||||
default:
|
||||
return Storage::disk($disk)->download($filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,11 @@ class StorageHelper
|
||||
* to determine that they are safe to display inline.
|
||||
*
|
||||
* @author <A. Gianotto> [<snipe@snipe.net]>
|
||||
* @since v7.0.14
|
||||
* @param $file_with_path
|
||||
* @since v7.0.14
|
||||
* @param $file_with_path
|
||||
* @return bool
|
||||
*/
|
||||
public static function allowSafeInline($file_with_path)
|
||||
{
|
||||
public static function allowSafeInline($file_with_path) {
|
||||
|
||||
$allowed_inline = [
|
||||
'pdf',
|
||||
@@ -49,7 +48,6 @@ class StorageHelper
|
||||
'avif',
|
||||
'webp',
|
||||
'png',
|
||||
'gif',
|
||||
];
|
||||
|
||||
|
||||
@@ -61,24 +59,10 @@ class StorageHelper
|
||||
|
||||
}
|
||||
|
||||
public static function getFiletype($file_with_path)
|
||||
{
|
||||
|
||||
// The file exists and is allowed to be displayed inline
|
||||
if (Storage::exists($file_with_path)) {
|
||||
return pathinfo($file_with_path, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decide whether to show the file inline or download it.
|
||||
*/
|
||||
public static function showOrDownloadFile($file, $filename)
|
||||
{
|
||||
public static function showOrDownloadFile($file, $filename) {
|
||||
|
||||
$headers = [];
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Events\CheckoutDeclined;
|
||||
use App\Events\ItemAccepted;
|
||||
use App\Events\ItemDeclined;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\CheckoutAcceptanceResponseMail;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
@@ -22,10 +21,8 @@ use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Notifications\AcceptanceAssetAcceptedNotification;
|
||||
use App\Notifications\AcceptanceAssetDeclinedNotification;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\SettingsController;
|
||||
@@ -340,21 +337,6 @@ class AcceptanceController extends Controller
|
||||
$return_msg = trans('admin/users/message.declined');
|
||||
}
|
||||
|
||||
if ($acceptance->alert_on_response_id) {
|
||||
try {
|
||||
$recipient = User::find($acceptance->alert_on_response_id);
|
||||
|
||||
if ($recipient) {
|
||||
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
|
||||
$acceptance,
|
||||
$recipient,
|
||||
$request->input('asset_acceptance') === 'accepted',
|
||||
));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::warning($e);
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to('account/accept')->with('success', $return_msg);
|
||||
|
||||
|
||||
200
app/Http/Controllers/Api/AssetFilesController.php
Normal file
200
app/Http/Controllers/Api/AssetFilesController.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Transformers\UploadedFilesTransformer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $asset = Asset::find($assetId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assets')) {
|
||||
Storage::makeDirectory('private_uploads/assets', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
|
||||
|
||||
$asset->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function list(Asset $asset, Request $request) : JsonResponse | array
|
||||
{
|
||||
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'filename',
|
||||
'eol',
|
||||
'notes',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')->where('item_type', '=', Asset::class)->where('item_id', '=', $asset->id);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$files = $files->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
$offset = ($request->input('offset') > $files->count()) ? $files->count() : abs($request->input('offset'));
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$files = $files->orderBy($sort, $order);
|
||||
|
||||
$files = $files->skip($offset)->take($limit)->get();
|
||||
return (new UploadedFilesTransformer())->transformFiles($files, $files->count());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function show(Asset $asset, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assets/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetId
|
||||
* @param int $fileId
|
||||
* @since [v6.0]
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
public function destroy(Asset $asset, $fileId = null) : JsonResponse
|
||||
{
|
||||
|
||||
$rel_path = 'private_uploads/assets';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($asset->id)) {
|
||||
$this->authorize('update', $asset);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,6 @@ class AssetMaintenancesController extends Controller
|
||||
'serial',
|
||||
'created_by',
|
||||
'supplier',
|
||||
'location',
|
||||
'is_warranty',
|
||||
'status_label',
|
||||
];
|
||||
@@ -99,9 +98,6 @@ class AssetMaintenancesController extends Controller
|
||||
case 'serial':
|
||||
$maintenances = $maintenances->OrderByAssetSerial($order);
|
||||
break;
|
||||
case 'location':
|
||||
$maintenances = $maintenances->OrderLocationName($order);
|
||||
break;
|
||||
case 'status_label':
|
||||
$maintenances = $maintenances->OrderStatusName($order);
|
||||
break;
|
||||
|
||||
184
app/Http/Controllers/Api/AssetModelFilesController.php
Normal file
184
app/Http/Controllers/Api/AssetModelFilesController.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\StorageHelper;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\AssetModelsTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
|
||||
/**
|
||||
* This class controls file related actions related
|
||||
* to assets for the Snipe-IT Asset Management application.
|
||||
*
|
||||
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v1.0
|
||||
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
|
||||
*/
|
||||
class AssetModelFilesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param int $assetModelId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// Make sure we are allowed to update this asset
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// If the file storage directory doesn't exist; create it
|
||||
if (! Storage::exists('private_uploads/assetmodels')) {
|
||||
Storage::makeDirectory('private_uploads/assetmodels', 775);
|
||||
}
|
||||
|
||||
// Loop over the attached files and add them to the asset
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
|
||||
|
||||
$assetModel->logUpload($file_name, e($request->get('notes')));
|
||||
}
|
||||
|
||||
// All done - report success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
|
||||
}
|
||||
|
||||
// We only reach here if no files were included in the POST, so tell the user this
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files for an asset.
|
||||
*
|
||||
* @param int $assetmodel
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function list($assetmodel_id) : JsonResponse | array
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetmodel_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
|
||||
$this->authorize('view', $assetmodel);
|
||||
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('view', $assetModel);
|
||||
|
||||
// Check that the file being requested exists for the asset
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
// Form the full filename with path
|
||||
$file = 'private_uploads/assetmodels/'.$log->filename;
|
||||
Log::debug('Checking for '.$file);
|
||||
|
||||
if ($log->action_type == 'audit') {
|
||||
$file = 'private_uploads/audits/'.$log->filename;
|
||||
}
|
||||
|
||||
// Check the file actually exists on the filesystem
|
||||
if (! Storage::exists($file)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
|
||||
return Storage::download($file, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader($file);
|
||||
}
|
||||
|
||||
// Send back an error message
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param int $assetModelId
|
||||
* @param int $fileId
|
||||
* @since [v7.0.12]
|
||||
* @author [r-xyz]
|
||||
*/
|
||||
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
|
||||
{
|
||||
// Start by checking if the asset being acted upon exists
|
||||
if (! $assetModel = AssetModel::find($assetModelId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
$rel_path = 'private_uploads/assetmodels';
|
||||
|
||||
// the asset is valid
|
||||
if (isset($assetModel->id)) {
|
||||
$this->authorize('update', $assetModel);
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($fileId);
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists($rel_path.'/'.$log->filename)) {
|
||||
Storage::delete($rel_path.'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
$log->delete();
|
||||
|
||||
// All deleting done - notify the user of success
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
|
||||
}
|
||||
}
|
||||
@@ -85,12 +85,6 @@ class AssetModelsController extends Controller
|
||||
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
|
||||
}
|
||||
|
||||
if ($request->input('requestable') == 'true') {
|
||||
$assetmodels = $assetmodels->where('models.requestable', '=', '1');
|
||||
} elseif ($request->input('requestable') == 'false') {
|
||||
$assetmodels = $assetmodels->where('models.requestable', '=', '0');
|
||||
}
|
||||
|
||||
if ($request->filled('notes')) {
|
||||
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
|
||||
}
|
||||
|
||||
@@ -114,7 +114,6 @@ class AssetsController extends Controller
|
||||
'byod',
|
||||
'asset_eol_date',
|
||||
'requestable',
|
||||
'jobtitle',
|
||||
];
|
||||
|
||||
$filter = [];
|
||||
@@ -396,9 +395,6 @@ class AssetsController extends Controller
|
||||
case 'assigned_to':
|
||||
$assets->OrderAssigned($order);
|
||||
break;
|
||||
case 'jobtitle':
|
||||
$assets->OrderByJobTitle($order);
|
||||
break;
|
||||
case 'created_by':
|
||||
$assets->OrderByCreatedByName($order);
|
||||
break;
|
||||
@@ -1150,7 +1146,7 @@ class AssetsController extends Controller
|
||||
|
||||
// Validate the rest of the data before we turn off the event dispatcher
|
||||
if ($asset->isInvalid()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag' => $asset->asset_tag], $asset->getErrors()));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -234,15 +234,6 @@ class ImportController extends Controller
|
||||
case 'location':
|
||||
$redirectTo = 'locations.index';
|
||||
break;
|
||||
case 'supplier':
|
||||
$redirectTo = 'suppliers.index';
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$redirectTo = 'manufacturers.index';
|
||||
break;
|
||||
case 'category':
|
||||
$redirectTo = 'categories.index';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
|
||||
@@ -87,8 +87,7 @@ class LocationsController extends Controller
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->with('adminuser');
|
||||
->withCount('users as users_count');
|
||||
|
||||
// Only scope locations if the setting is enabled
|
||||
if (Setting::getSettings()->scope_locations_fmcs) {
|
||||
@@ -219,7 +218,6 @@ class LocationsController extends Controller
|
||||
'locations.updated_at',
|
||||
'locations.image',
|
||||
'locations.currency',
|
||||
'locations.company_id',
|
||||
'locations.notes',
|
||||
])
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
|
||||
@@ -4,19 +4,15 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ProfileTransformer;
|
||||
use App\Models\CheckoutRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use App\Models\CustomField;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
@@ -171,22 +167,6 @@ class ProfileController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the EULAs accepted by the user.
|
||||
*
|
||||
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*@since [v8.1.16]
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
*/
|
||||
public function eulas(ProfileTransformer $transformer)
|
||||
{
|
||||
// Only return this user's EULAs
|
||||
$eulas = auth()->user()->eulas;
|
||||
return response()->json(
|
||||
$transformer->transformFiles($eulas, $eulas->count())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,15 +24,10 @@ class SuppliersController extends Controller
|
||||
public function index(Request $request): array
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
$allowed_columns = ['
|
||||
id',
|
||||
'name',
|
||||
'address',
|
||||
'address2',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'phone',
|
||||
'contact',
|
||||
'fax',
|
||||
@@ -44,24 +39,21 @@ class SuppliersController extends Controller
|
||||
'components_count',
|
||||
'consumables_count',
|
||||
'url',
|
||||
'notes',
|
||||
];
|
||||
|
||||
$suppliers = Supplier::select(
|
||||
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'created_by', 'updated_at', 'deleted_at', 'image', 'notes', 'url', 'zip'])
|
||||
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes', 'url'])
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('licenses as licenses_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('components as components_count')
|
||||
->withCount('consumables as consumables_count')
|
||||
->with('adminuser');
|
||||
->withCount('consumables as consumables_count');
|
||||
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers->TextSearch($request->input('search'));
|
||||
$suppliers = $suppliers->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$suppliers->where('name', '=', $request->input('name'));
|
||||
}
|
||||
@@ -108,15 +100,7 @@ class SuppliersController extends Controller
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'created_by':
|
||||
$suppliers->OrderByCreatedByName($order);
|
||||
break;
|
||||
default:
|
||||
$suppliers->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
$suppliers->orderBy($sort, $order);
|
||||
|
||||
$total = $suppliers->count();
|
||||
$suppliers = $suppliers->skip($offset)->take($limit)->get();
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use App\Http\Transformers\UploadedFilesTransformer;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
|
||||
class UploadedFilesController extends Controller
|
||||
{
|
||||
|
||||
|
||||
static $map_object_type = [
|
||||
'accessories' => Accessory::class,
|
||||
'assets' => Asset::class,
|
||||
'components' => Component::class,
|
||||
'consumables' => Consumable::class,
|
||||
'licenses' => License::class,
|
||||
'locations' => Location::class,
|
||||
'models' => AssetModel::class,
|
||||
'users' => User::class,
|
||||
];
|
||||
|
||||
static $map_storage_path = [
|
||||
'accessories' => 'private_uploads/accessories/',
|
||||
'assets' => 'private_uploads/assets/',
|
||||
'components' => 'private_uploads/components/',
|
||||
'consumables' => 'private_uploads/consumables/',
|
||||
'licenses' => 'private_uploads/licenses/',
|
||||
'locations' => 'private_uploads/locations/',
|
||||
'models' => 'private_uploads/assetmodels/',
|
||||
'users' => 'private_uploads/users/',
|
||||
];
|
||||
|
||||
static $map_file_prefix= [
|
||||
'accessories' => 'accessory',
|
||||
'assets' => 'asset',
|
||||
'components' => 'component',
|
||||
'consumables' => 'consumable',
|
||||
'licenses' => 'license',
|
||||
'locations' => 'location',
|
||||
'models' => 'model',
|
||||
'users' => 'user',
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List files for an object
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to list files for
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function index(Request $request, $object_type, $id) : JsonResponse | array
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// Columns allowed for sorting
|
||||
$allowed_columns =
|
||||
[
|
||||
'id',
|
||||
'filename',
|
||||
'action_type',
|
||||
'note',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
$uploads = $object->uploads();
|
||||
$offset = ($request->input('offset') > $object->count()) ? $object->count() : abs($request->input('offset'));
|
||||
$limit = app('api_limit_value');
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'action_logs.created_at';
|
||||
|
||||
// Text search on action_logs fields
|
||||
// We could use the normal Actionlogs text scope, but it's a very heavy query since it's searcghing across all relations
|
||||
// And we generally won't need that here
|
||||
if ($request->filled('search')) {
|
||||
|
||||
$uploads->where(
|
||||
function ($query) use ($request) {
|
||||
$query->where('filename', 'LIKE', '%' . $request->input('search') . '%')
|
||||
->orWhere('note', 'LIKE', '%' . $request->input('search') . '%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
|
||||
return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accepts a POST to upload a file to the server.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to store so we can check permisisons
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function store(UploadFileRequest $request, $object_type, $id) : JsonResponse
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
// If the file storage directory doesn't exist, create it
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type])) {
|
||||
Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
|
||||
}
|
||||
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
// Loop over the attached files and add them to the object
|
||||
foreach ($request->file('file') as $file) {
|
||||
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
|
||||
$files[] = $file_name;
|
||||
$object->logUpload($file_name, $request->get('notes'));
|
||||
}
|
||||
|
||||
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
|
||||
->where('item_type', '=', self::$map_object_type[$object_type])
|
||||
->where('item_id', '=', $id)->whereIn('filename', $files)
|
||||
->get();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
|
||||
}
|
||||
|
||||
// No files were submitted
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check for permissions and display the file.
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to delete from so we can check permisisons
|
||||
* @param $file_id the ID of the file to delete from the action_logs table
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
|
||||
{
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('view', $object);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check that the file being requested exists for the object
|
||||
if (! $log = Actionlog::whereNotNull('filename')->where('item_type', self::$map_object_type[$object_type])->where('item_id', $object->id)->find($file_id)
|
||||
) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 200);
|
||||
}
|
||||
|
||||
|
||||
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200));
|
||||
}
|
||||
|
||||
if (request('inline') == 'true') {
|
||||
$headers = [
|
||||
'Content-Disposition' => 'inline',
|
||||
];
|
||||
return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
|
||||
}
|
||||
|
||||
return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associated file
|
||||
*
|
||||
* @param \App\Http\Requests\UploadFileRequest $request
|
||||
* @param string $object_type the type of object to upload the file to
|
||||
* @param int $id the ID of the object to delete from so we can check permisisons
|
||||
* @param $file_id the ID of the file to delete from the action_logs table
|
||||
* @since [v8.1.17]
|
||||
* @author [A. Gianotto <snipe@snipe.net>]
|
||||
*/
|
||||
public function destroy($object_type, $id, $file_id) : JsonResponse
|
||||
{
|
||||
|
||||
// Check the permissions to make sure the user can view the object
|
||||
$object = self::$map_object_type[$object_type]::find($id);
|
||||
$this->authorize('update', self::$map_object_type[$object_type]);
|
||||
|
||||
if (!$object) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
|
||||
}
|
||||
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
|
||||
->where('item_id', $object->id)->first();
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
|
||||
Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
|
||||
}
|
||||
// Delete the record of the file
|
||||
if ($log->delete()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// The file doesn't seem to really exist, so report an error
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\SaveUserRequest;
|
||||
use App\Http\Transformers\AccessoriesTransformer;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\ConsumablesTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
@@ -81,7 +80,7 @@ class UsersController extends Controller
|
||||
'users.autoassign_licenses',
|
||||
'users.website',
|
||||
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations', 'eulas')
|
||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
|
||||
->withCount([
|
||||
'assets as assets_count' => function(Builder $query) {
|
||||
$query->withoutTrashed();
|
||||
@@ -207,11 +206,11 @@ class UsersController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('manages_users_count')) {
|
||||
$users->has('managesUsers', '=', $request->input('manages_users_count'));
|
||||
$users->has('manages_users_count', '=', $request->input('manages_users_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('manages_locations_count')) {
|
||||
$users->has('managedLocations', '=', $request->input('manages_locations_count'));
|
||||
$users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
|
||||
}
|
||||
|
||||
if ($request->filled('autoassign_licenses')) {
|
||||
@@ -677,6 +676,7 @@ class UsersController extends Controller
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
if ($user = User::where('id', $id)->withTrashed()->first()) {
|
||||
$this->authorize('update', $user);
|
||||
$licenses = $user->licenses()->get();
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
@@ -736,25 +736,6 @@ class UsersController extends Controller
|
||||
return (new UsersTransformer)->transformUser($request->user());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the EULAs accepted by the user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*@since [v8.1.16]
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
*/
|
||||
public function eulas(User $user, ActionlogsTransformer $transformer)
|
||||
{
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$eulas = $user->eulas;
|
||||
return response()->json(
|
||||
$transformer->transformActionlogs($eulas, $eulas->count())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a soft-deleted user.
|
||||
*
|
||||
|
||||
@@ -19,6 +19,19 @@ use \Illuminate\Http\RedirectResponse;
|
||||
*/
|
||||
class AssetMaintenancesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Checks for permissions for this action.
|
||||
*
|
||||
* @todo This should be replaced with middleware and/or policies
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
private static function getInsufficientPermissionsRedirect(): RedirectResponse
|
||||
{
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that invokes the ajax tables which actually contains
|
||||
@@ -54,10 +67,16 @@ class AssetMaintenancesController extends Controller
|
||||
// We have to set this so that the correct property is set in the select2 ajax dropdown
|
||||
$asset->asset_id = $asset->id;
|
||||
}
|
||||
|
||||
|
||||
// Prepare Asset Maintenance Type List
|
||||
$assetMaintenanceType = [
|
||||
'' => 'Select an asset maintenance type',
|
||||
] + AssetMaintenance::getImprovementOptions();
|
||||
// Mark the selected asset, if it came in
|
||||
|
||||
return view('asset_maintenances/edit')
|
||||
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
|
||||
->with('asset', $asset)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', new AssetMaintenance);
|
||||
}
|
||||
|
||||
@@ -72,45 +91,43 @@ class AssetMaintenancesController extends Controller
|
||||
public function store(Request $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// create a new model instance
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
$asset = Asset::find($request->input('asset_id'));
|
||||
|
||||
$assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
|
||||
|
||||
// Loop through the selected assets
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$assetMaintenance = new AssetMaintenance();
|
||||
$assetMaintenance->supplier_id = $request->input('supplier_id');
|
||||
$assetMaintenance->is_warranty = $request->input('is_warranty');
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = $request->input('notes');
|
||||
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $asset->id;
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->created_by = auth()->id();
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if (!$assetMaintenance->save()) {
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
}
|
||||
if ((! Company::isCurrentUserHasAccess($asset)) && ($asset != null)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
// Save the asset maintenance data
|
||||
$assetMaintenance->asset_id = $request->input('asset_id');
|
||||
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$assetMaintenance->title = $request->input('title');
|
||||
$assetMaintenance->start_date = $request->input('start_date');
|
||||
$assetMaintenance->completion_date = $request->input('completion_date');
|
||||
$assetMaintenance->created_by = auth()->id();
|
||||
|
||||
if (($assetMaintenance->completion_date !== null)
|
||||
&& ($assetMaintenance->start_date !== '')
|
||||
&& ($assetMaintenance->start_date !== '0000-00-00')
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.create.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,19 +135,26 @@ class AssetMaintenancesController extends Controller
|
||||
*
|
||||
* @see AssetMaintenancesController::postEdit() method that stores the data
|
||||
* @author Vincent Sposato <vincent.sposato@gmail.com>
|
||||
* @param int $assetMaintenanceId
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Prepare Improvement Type List
|
||||
$assetMaintenanceType = ['' => 'Select an improvement type'] + AssetMaintenance::getImprovementOptions();
|
||||
|
||||
return view('asset_maintenances/edit')
|
||||
->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
|
||||
->with('asset_ids', request()->input('asset_ids', []))
|
||||
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
|
||||
->with('item', $maintenance);
|
||||
->with('selectedAsset', null)
|
||||
->with('assetMaintenanceType', $assetMaintenanceType)
|
||||
->with('item', $maintenance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,21 +170,33 @@ class AssetMaintenancesController extends Controller
|
||||
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
|
||||
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
|
||||
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
$maintenance->supplier_id = $request->input('supplier_id');
|
||||
$maintenance->is_warranty = $request->input('is_warranty', 0);
|
||||
$maintenance->is_warranty = $request->input('is_warranty');
|
||||
$maintenance->cost = $request->input('cost');
|
||||
$maintenance->notes = $request->input('notes');
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Save the asset maintenance data
|
||||
$maintenance->asset_id = $request->input('asset_id');
|
||||
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
|
||||
$maintenance->title = $request->input('title');
|
||||
$maintenance->start_date = $request->input('start_date');
|
||||
$maintenance->completion_date = $request->input('completion_date');
|
||||
|
||||
|
||||
// Todo - put this in a getter/setter?
|
||||
if (($maintenance->completion_date == null))
|
||||
{
|
||||
if (($maintenance->completion_date == null)
|
||||
) {
|
||||
if (($maintenance->asset_maintenance_time !== 0)
|
||||
|| (! is_null($maintenance->asset_maintenance_time))
|
||||
) {
|
||||
@@ -177,7 +213,10 @@ class AssetMaintenancesController extends Controller
|
||||
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($maintenance->save()) {
|
||||
|
||||
// Redirect to the new asset maintenance page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.edit.success'));
|
||||
}
|
||||
@@ -193,12 +232,21 @@ class AssetMaintenancesController extends Controller
|
||||
* @version v1.0
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function destroy(AssetMaintenance $maintenance) : RedirectResponse
|
||||
public function destroy($assetMaintenanceId) : RedirectResponse
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
$this->authorize('update', $maintenance->asset);
|
||||
// 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'));
|
||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
// Delete the asset maintenance
|
||||
$maintenance->delete();
|
||||
$assetMaintenance->delete();
|
||||
|
||||
// Redirect to the asset_maintenance management page
|
||||
return redirect()->route('maintenances.index')
|
||||
->with('success', trans('admin/asset_maintenances/message.delete.success'));
|
||||
@@ -214,6 +262,11 @@ class AssetMaintenancesController extends Controller
|
||||
*/
|
||||
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
|
||||
return static::getInsufficientPermissionsRedirect();
|
||||
}
|
||||
|
||||
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ class AssetCheckinController extends Controller
|
||||
});
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkin = now();
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
$asset->name = $request->get('name');
|
||||
@@ -122,14 +123,11 @@ class AssetCheckinController extends Controller
|
||||
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
// Handle last checkin date
|
||||
$checkin_at = date('Y-m-d H:i:s');
|
||||
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
|
||||
$originalValues['action_date'] = $checkin_at;
|
||||
$checkin_at = $request->get('checkin_at');
|
||||
|
||||
}
|
||||
$asset->last_checkin = $checkin_at;
|
||||
|
||||
$asset->licenseseats->each(function (LicenseSeat $seat) {
|
||||
$seat->update(['assigned_to' => null]);
|
||||
|
||||
@@ -149,7 +149,7 @@ class AssetsController extends Controller
|
||||
$asset->byod = request('byod', 0);
|
||||
|
||||
if (! empty($settings->audit_interval)) {
|
||||
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
|
||||
$asset->next_audit_date = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
}
|
||||
|
||||
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
|
||||
@@ -188,31 +188,14 @@ class AssetsController extends Controller
|
||||
|
||||
// Validate the asset before saving
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
$target = null;
|
||||
$location = null;
|
||||
|
||||
if ($userId = request('assigned_user')) {
|
||||
$target = User::find($userId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
|
||||
}
|
||||
if (request('assigned_user')) {
|
||||
$target = User::find(request('assigned_user'));
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($assetId = request('assigned_asset')) {
|
||||
$target = Asset::find($assetId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
|
||||
}
|
||||
} elseif (request('assigned_asset')) {
|
||||
$target = Asset::find(request('assigned_asset'));
|
||||
$location = $target->location_id;
|
||||
|
||||
} elseif ($locationId = request('assigned_location')) {
|
||||
$target = Location::find($locationId);
|
||||
|
||||
if (!$target) {
|
||||
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
|
||||
}
|
||||
} elseif (request('assigned_location')) {
|
||||
$target = Location::find(request('assigned_location'));
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
@@ -463,7 +446,7 @@ class AssetsController extends Controller
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues));
|
||||
DB::table('assets')
|
||||
->where('id', $asset->id)
|
||||
->update(['assigned_to' => null, 'assigned_type' => null]);
|
||||
->update(['assigned_to' => null]);
|
||||
}
|
||||
|
||||
|
||||
@@ -475,7 +458,6 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$asset->delete();
|
||||
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
|
||||
@@ -908,7 +890,7 @@ class AssetsController extends Controller
|
||||
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
$dt = Carbon::now()->addMonths( (int) $settings->audit_interval)->toDateString();
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
|
||||
@@ -52,26 +52,11 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
$asset_ids = $request->input('ids');
|
||||
|
||||
if ($request->input('bulk_actions') === 'checkout') {
|
||||
$status_check =$this->hasUndeployableStatus($asset_ids);
|
||||
if($status_check && $status_check['status'] === true){
|
||||
|
||||
$asset_tags = implode(', ', array_column($status_check['tags'], 'asset_tag'));
|
||||
$asset_ids = $status_check['asset_ids'];
|
||||
|
||||
session()->flash('warning', trans('admin/hardware/message.undeployable', ['asset_tags' => $asset_tags]));
|
||||
}
|
||||
|
||||
$request->session()->flashInput(['selected_assets' => $asset_ids]);
|
||||
return redirect()->route('hardware.bulkcheckout.show');
|
||||
}
|
||||
|
||||
if ($request->input('bulk_actions') === 'maintenance') {
|
||||
$request->session()->flashInput(['selected_assets' => $asset_ids]);
|
||||
return redirect()->route('maintenances.create');
|
||||
}
|
||||
|
||||
// Figure out where we need to send the user after the update is complete, and store that in the session
|
||||
$bulk_back_url = request()->headers->get('referer');
|
||||
session(['bulk_back_url' => $bulk_back_url]);
|
||||
@@ -229,21 +214,6 @@ class BulkAssetsController extends Controller
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
// find custom field input attributes that start with 'null_'
|
||||
$null_custom_fields_inputs = array_filter($request->all(), function ($key) {
|
||||
// filter out all keys that start with 'null_'
|
||||
return (strpos($key, 'null_') === 0);
|
||||
}, ARRAY_FILTER_USE_KEY);;
|
||||
// remove 'null' from the keys
|
||||
$custom_fields_to_null = [];
|
||||
foreach ($null_custom_fields_inputs as $key => $value) {
|
||||
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
@@ -281,9 +251,7 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('null_expected_checkin_date'))
|
||||
|| ($request->filled('null_next_audit_date'))
|
||||
|| ($request->filled('null_asset_eol_date'))
|
||||
|| ($request->filled('null_notes'))
|
||||
|| ($request->anyFilled($custom_field_columns))
|
||||
|| ($request->anyFilled(array_keys($null_custom_fields_inputs)))
|
||||
|
||||
) {
|
||||
// Let's loop through those assets and build an update array
|
||||
@@ -306,14 +274,10 @@ class BulkAssetsController extends Controller
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months')
|
||||
->conditionallyAddItem('next_audit_date')
|
||||
->conditionallyAddItem('asset_eol_date')
|
||||
->conditionallyAddItem('notes');
|
||||
->conditionallyAddItem('asset_eol_date');
|
||||
foreach ($custom_field_columns as $key => $custom_field_column) {
|
||||
$this->conditionallyAddItem($custom_field_column);
|
||||
}
|
||||
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
|
||||
$this->conditionallyAddItem($key);
|
||||
}
|
||||
|
||||
if (!($asset->eol_explicit)) {
|
||||
if ($request->filled('model_id')) {
|
||||
@@ -364,10 +328,6 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->input('null_notes')=='1') {
|
||||
$this->update_array['notes'] = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($request->filled('purchase_cost')) {
|
||||
@@ -408,12 +368,10 @@ class BulkAssetsController extends Controller
|
||||
// This could probably be added to a form request.
|
||||
// If the asset isn't assigned, we don't care what the status is.
|
||||
// Otherwise we need to make sure the status type is still a deployable one.
|
||||
|
||||
$unassigned = $asset->assigned_to == '';
|
||||
$deployable = $updated_status->deployable == '1' && $asset->assetstatus?->deployable == '1';
|
||||
$pending = $updated_status->pending === 1;
|
||||
|
||||
if ($unassigned || $deployable || $pending) {
|
||||
if (
|
||||
($asset->assigned_to == '')
|
||||
|| ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
|
||||
) {
|
||||
$this->update_array['status_id'] = $updated_status->id;
|
||||
}
|
||||
|
||||
@@ -465,7 +423,6 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Start all the custom fields shenanigans
|
||||
*/
|
||||
|
||||
@@ -473,15 +430,6 @@ class BulkAssetsController extends Controller
|
||||
if ($asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
// null custom fields
|
||||
if ($custom_fields_to_null) {
|
||||
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
|
||||
if ($field->db_column == $key) {
|
||||
$this->update_array[$field->db_column] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
if (Gate::allows('admin')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
@@ -614,10 +562,7 @@ class BulkAssetsController extends Controller
|
||||
public function showCheckout() : View
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
|
||||
$do_not_change = ['' => trans('general.do_not_change')];
|
||||
$status_label_list = $do_not_change + Helper::deployableStatusLabelList();
|
||||
return view('hardware/bulk-checkout')->with('statusLabel_list', $status_label_list);
|
||||
return view('hardware/bulk-checkout');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -649,13 +594,13 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
$checkout_at = date('Y-m-d H:i:s');
|
||||
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
|
||||
$checkout_at = $request->get('checkout_at');
|
||||
$checkout_at = e($request->get('checkout_at'));
|
||||
}
|
||||
|
||||
$expected_checkin = '';
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$expected_checkin = $request->get('expected_checkin');
|
||||
$expected_checkin = e($request->get('expected_checkin'));
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
@@ -663,11 +608,6 @@ class BulkAssetsController extends Controller
|
||||
foreach ($assets as $asset) {
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
// See if there is a status label passed
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->get('status_id');
|
||||
}
|
||||
|
||||
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
|
||||
|
||||
//TODO - I think this logic is duplicated in the checkOut method?
|
||||
@@ -711,25 +651,4 @@ class BulkAssetsController extends Controller
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
|
||||
}
|
||||
}
|
||||
public function hasUndeployableStatus (array $asset_ids)
|
||||
{
|
||||
$undeployable = Asset::whereIn('id', $asset_ids)
|
||||
->undeployable()
|
||||
->get();
|
||||
|
||||
$undeployableTags = $undeployable->map(function ($asset) {
|
||||
return [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
$undeployableIds = array_column($undeployableTags, 'id');
|
||||
$filtered_ids = array_diff($asset_ids, $undeployableIds);
|
||||
|
||||
if($undeployable->isNotEmpty()) {
|
||||
return ['status' => true, 'tags' => $undeployableTags, 'asset_ids' => $filtered_ids];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ class CategoriesController extends Controller
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->alert_on_response = $request->input('alert_on_response', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->notes = $request->input('notes');
|
||||
$category->created_by = auth()->id();
|
||||
@@ -122,7 +121,6 @@ class CategoriesController extends Controller
|
||||
$category->eula_text = $request->input('eula_text');
|
||||
$category->use_default_eula = $request->input('use_default_eula', '0');
|
||||
$category->require_acceptance = $request->input('require_acceptance', '0');
|
||||
$category->alert_on_response = $request->input('alert_on_response', '0');
|
||||
$category->checkin_email = $request->input('checkin_email', '0');
|
||||
$category->notes = $request->input('notes');
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class LicenseCheckinController extends Controller
|
||||
}
|
||||
|
||||
if($licenseSeat->assigned_to != null){
|
||||
$return_to = User::withTrashed()->find($licenseSeat->assigned_to);
|
||||
$return_to = User::find($licenseSeat->assigned_to);
|
||||
session()->put('checkedInFrom', $return_to->id);
|
||||
} else {
|
||||
$return_to = Asset::find($licenseSeat->asset_id);
|
||||
|
||||
@@ -3,21 +3,15 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\ProfileTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Profiles for
|
||||
* the Snipe-IT Asset Management application.
|
||||
@@ -226,7 +220,7 @@ class ProfileController extends Controller
|
||||
|
||||
if (!$user = User::find(auth()->id())) {
|
||||
return redirect()->back()
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
|
||||
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
|
||||
}
|
||||
if (empty($user->email)) {
|
||||
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
|
||||
@@ -240,28 +234,4 @@ class ProfileController extends Controller
|
||||
|
||||
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
|
||||
{
|
||||
|
||||
$logentry = Actionlog::where('filename', $filename)->first();
|
||||
|
||||
// Make sure the user has permission to view this file
|
||||
if (auth()->id() != $logentry->target_id) {
|
||||
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
|
||||
}
|
||||
|
||||
if (config('filesystems.default') == 's3_private') {
|
||||
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
|
||||
}
|
||||
|
||||
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
|
||||
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class ReportsController extends Controller
|
||||
$currency = e(Setting::getSettings()->default_currency);
|
||||
}
|
||||
|
||||
$row[] = Helper::getFormattedDateObject($asset->purchase_date, 'date', false);
|
||||
$row[] = $asset->purchase_date;
|
||||
$row[] = $currency.Helper::formatCurrencyOutput($asset->purchase_cost);
|
||||
$row[] = $currency.Helper::formatCurrencyOutput($asset->getDepreciatedValue());
|
||||
$row[] = $currency.Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
|
||||
|
||||
@@ -352,7 +352,6 @@ class SettingsController extends Controller
|
||||
$setting->dash_chart_type = $request->input('dash_chart_type');
|
||||
$setting->profile_edit = $request->input('profile_edit', 0);
|
||||
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
|
||||
$setting->manager_view_enabled = $request->input('manager_view_enabled', 0);
|
||||
|
||||
|
||||
if ($request->input('per_page') != '') {
|
||||
@@ -651,7 +650,6 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->alert_email = $alert_email;
|
||||
$setting->admin_cc_email = $admin_cc_email;
|
||||
$setting->admin_cc_always = $request->validated('admin_cc_always');
|
||||
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
|
||||
$setting->alert_interval = $request->input('alert_interval');
|
||||
$setting->alert_threshold = $request->input('alert_threshold');
|
||||
@@ -924,7 +922,7 @@ class SettingsController extends Controller
|
||||
* @since v5.0.0
|
||||
*/
|
||||
public function postSamlSettings(SettingsSamlRequest $request) : RedirectResponse
|
||||
{
|
||||
{
|
||||
if (is_null($setting = Setting::getSettings())) {
|
||||
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
|
||||
}
|
||||
|
||||
@@ -27,126 +27,50 @@ use Exception;
|
||||
class ViewAssetsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Extract custom fields that should be displayed in user view.
|
||||
*
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
private function extractCustomFields(User $user): array
|
||||
{
|
||||
$fieldArray = [];
|
||||
foreach ($user->assets as $asset) {
|
||||
if ($asset->model && $asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
if ($field->display_in_user_view == '1') {
|
||||
$fieldArray[$field->db_column] = $field->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_unique($fieldArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of users viewable by the current user.
|
||||
*
|
||||
* @param User $authUser
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function getViewableUsers(User $authUser): \Illuminate\Support\Collection
|
||||
{
|
||||
// SuperAdmin sees all users
|
||||
if ($authUser->isSuperUser()) {
|
||||
return User::select('id', 'first_name', 'last_name', 'username')
|
||||
->where('activated', 1)
|
||||
->orderBy('last_name')
|
||||
->orderBy('first_name')
|
||||
->get();
|
||||
}
|
||||
|
||||
// Regular manager sees only their subordinates + self
|
||||
$managedUsers = $authUser->getAllSubordinates();
|
||||
|
||||
// If user has subordinates, show them with self at beginning
|
||||
if ($managedUsers->count() > 0) {
|
||||
return collect([$authUser])->merge($managedUsers)
|
||||
->sortBy('last_name')
|
||||
->sortBy('first_name');
|
||||
}
|
||||
|
||||
// User has no subordinates, only sees themselves
|
||||
return collect([$authUser]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected user ID from request or default to current user.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Illuminate\Support\Collection $subordinates
|
||||
* @param int $defaultUserId
|
||||
* @return int
|
||||
*/
|
||||
private function getSelectedUserId(Request $request, \Illuminate\Support\Collection $subordinates, int $defaultUserId): int
|
||||
{
|
||||
// If no subordinates or no user_id in request, return default
|
||||
if ($subordinates->count() <= 1 || !$request->filled('user_id')) {
|
||||
return $defaultUserId;
|
||||
}
|
||||
|
||||
$requestedUserId = (int) $request->input('user_id');
|
||||
|
||||
// Validate if the requested user is allowed
|
||||
if ($subordinates->contains('id', $requestedUserId)) {
|
||||
return $requestedUserId;
|
||||
}
|
||||
|
||||
// If invalid ID or not authorized, return default
|
||||
return $defaultUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show user's assigned assets with optional manager view functionality.
|
||||
* Redirect to the profile page.
|
||||
*
|
||||
*/
|
||||
public function getIndex(Request $request) : View | RedirectResponse
|
||||
public function getIndex() : View | RedirectResponse
|
||||
{
|
||||
$authUser = auth()->user();
|
||||
$settings = Setting::getSettings();
|
||||
$subordinates = collect();
|
||||
$selectedUserId = $authUser->id;
|
||||
|
||||
// Process manager view if enabled
|
||||
if ($settings->manager_view_enabled) {
|
||||
$subordinates = $this->getViewableUsers($authUser);
|
||||
$selectedUserId = $this->getSelectedUserId($request, $subordinates, $authUser->id);
|
||||
}
|
||||
|
||||
// Load the data for the user to be viewed (either auth user or selected subordinate)
|
||||
$userToView = User::with([
|
||||
$user = User::with(
|
||||
'assets',
|
||||
'assets.model',
|
||||
'assets.model.fieldset.fields',
|
||||
'consumables',
|
||||
'accessories',
|
||||
'licenses'
|
||||
])->find($selectedUserId);
|
||||
'licenses',
|
||||
)->find(auth()->id());
|
||||
|
||||
$field_array = array();
|
||||
|
||||
// Loop through all the custom fields that are applied to any model the user has assigned
|
||||
foreach ($user->assets as $asset) {
|
||||
|
||||
// Make sure the model has a custom fieldset before trying to loop through the associated fields
|
||||
if ($asset->model->fieldset) {
|
||||
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
// check and make sure they're allowed to see the value of the custom field
|
||||
if ($field->display_in_user_view == '1') {
|
||||
$field_array[$field->db_column] = $field->name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If the user to view couldn't be found (shouldn't happen with proper logic), redirect with error
|
||||
if (!$userToView) {
|
||||
return redirect()->route('view-assets')->with('error', trans('admin/users/message.user_not_found'));
|
||||
}
|
||||
|
||||
// Process custom fields for the user being viewed
|
||||
$fieldArray = $this->extractCustomFields($userToView);
|
||||
// Since some models may re-use the same fieldsets/fields, let's make the array unique so we don't repeat columns
|
||||
array_unique($field_array);
|
||||
|
||||
// Pass the necessary data to the view
|
||||
return view('account/view-assets', [
|
||||
'user' => $userToView, // Use 'user' for compatibility with the existing view
|
||||
'field_array' => $fieldArray,
|
||||
'settings' => $settings,
|
||||
'subordinates' => $subordinates,
|
||||
'selectedUserId' => $selectedUserId
|
||||
]);
|
||||
if (isset($user->id)) {
|
||||
return view('account/view-assets', compact('user', 'field_array' ))
|
||||
->with('settings', Setting::getSettings());
|
||||
}
|
||||
|
||||
// Redirect to the user management page
|
||||
return redirect()->route('users.index')
|
||||
->with('error', trans('admin/users/message.user_not_found', $user->id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,7 +73,6 @@ class Kernel extends HttpKernel
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'api-throttle' => \App\Http\Middleware\SetAPIResponseHeaders::class,
|
||||
'health' => null,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SetAPIResponseHeaders extends ThrottleRequests
|
||||
{
|
||||
|
||||
/**
|
||||
* Add the rate limit headers to the response.
|
||||
*
|
||||
* This extends the original ThrottleRequests middleware to add the 'X-RateLimit-Reset' and 'Retry-After' headers, even
|
||||
* if the rate limit is not exceeded.
|
||||
* @param $maxAttempts
|
||||
* @param $remainingAttempts
|
||||
* @param $retryAfter
|
||||
* @param Response|null $response
|
||||
* @return array|int[]
|
||||
*/
|
||||
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null, ?Response $response = null)
|
||||
{
|
||||
if ($response &&
|
||||
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
|
||||
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
|
||||
$headers = [];
|
||||
$headers['Retry-After'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
|
||||
return $headers;
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'X-RateLimit-Limit' => $maxAttempts,
|
||||
'X-RateLimit-Remaining' => $remainingAttempts,
|
||||
];
|
||||
|
||||
if (! is_null($retryAfter)) {
|
||||
$headers['Retry-After'] = $retryAfter;
|
||||
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
|
||||
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
|
||||
$this->limiter->hit($limit->key, $limit->decaySeconds);
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts),
|
||||
$this->getTimeUntilNextRetry($limit->key) // this is the only line we changed
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,7 +41,6 @@ class SettingsSamlRequest extends FormRequest
|
||||
public function withValidator($validator)
|
||||
{
|
||||
$validator->after(function ($validator) {
|
||||
$setting = Setting::getSettings();
|
||||
if ($this->input('saml_enabled') == '1') {
|
||||
$idpMetadata = $this->input('saml_idp_metadata');
|
||||
if (! empty($idpMetadata)) {
|
||||
@@ -57,7 +56,7 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
$was_custom_x509cert = strpos($setting->saml_custom_settings, 'sp_x509cert') !== false;
|
||||
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
|
||||
|
||||
$custom_x509cert = '';
|
||||
$custom_privateKey = '';
|
||||
@@ -127,14 +126,10 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
|
||||
if (! (empty($x509cert) && empty($privateKey))) {
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509cert' => $x509cert,
|
||||
// 'saml_sp_privatekey' => $privateKey,
|
||||
// ]);
|
||||
$setting->saml_sp_x509cert = $x509cert;
|
||||
$setting->saml_sp_privatekey = $privateKey;
|
||||
$setting->save();
|
||||
|
||||
$this->merge([
|
||||
'saml_sp_x509cert' => $x509cert,
|
||||
'saml_sp_privatekey' => $privateKey,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
|
||||
@@ -150,21 +145,15 @@ class SettingsSamlRequest extends FormRequest
|
||||
}
|
||||
|
||||
if (! empty($x509certNew)) {
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509certNew' => $x509certNew,
|
||||
// ]);
|
||||
$setting->saml_sp_x509certNew = $x509certNew;
|
||||
$setting->save();
|
||||
$this->merge([
|
||||
'saml_sp_x509certNew' => $x509certNew,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// $this->merge([
|
||||
// 'saml_sp_x509certNew' => '',
|
||||
// ]);
|
||||
$setting->saml_sp_x509certNew = '';
|
||||
$setting->save();
|
||||
$this->merge([
|
||||
'saml_sp_x509certNew' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ class StoreLabelSettings extends FormRequest
|
||||
|
||||
return [
|
||||
'labels_per_page' => 'numeric',
|
||||
'labels_width' => 'numeric|min:0.1',
|
||||
'labels_height' => 'numeric|min:0.1',
|
||||
'labels_width' => 'numeric',
|
||||
'labels_height' => 'numeric',
|
||||
'labels_pmargin_left' => 'numeric|nullable',
|
||||
'labels_pmargin_right' => 'numeric|nullable',
|
||||
'labels_pmargin_top' => 'numeric|nullable',
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Http\Requests;
|
||||
use App\Models\Accessory;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreNotificationSettings extends FormRequest
|
||||
{
|
||||
@@ -27,9 +26,6 @@ class StoreNotificationSettings extends FormRequest
|
||||
return [
|
||||
'alert_email' => 'email_array|nullable',
|
||||
'admin_cc_email' => 'email_array|nullable',
|
||||
'admin_cc_always' => [
|
||||
Rule::in('0', '1'),
|
||||
],
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable|gt:0',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Http\Traits\ConvertsBase64ToFiles;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use \App\Helpers\Helper;
|
||||
|
||||
class UploadFileRequest extends Request
|
||||
{
|
||||
@@ -28,65 +27,44 @@ class UploadFileRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$max_file_size = Helper::file_upload_max_size();
|
||||
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
|
||||
|
||||
return [
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
|
||||
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes (if needed) and Saves a file to the appropriate location
|
||||
* Returns the 'short' (storage-relative) filename
|
||||
*
|
||||
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
|
||||
* a way to merge them or extend one into the other?
|
||||
*/
|
||||
public function handleFile(string $dirname, string $name_prefix, $file): string
|
||||
{
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
|
||||
|
||||
// Check for SVG and sanitize it
|
||||
if ($file->getMimeType() === 'image/svg+xml') {
|
||||
$uploaded_file = $this->handleSVG($file);
|
||||
Log::debug('This is an SVG');
|
||||
Log::debug($file_name);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
$cleanSVG = $sanitizer->sanitize($dirtySVG);
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $cleanSVG);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug('Upload no workie :( ');
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
} else {
|
||||
$uploaded_file = file_get_contents($file);
|
||||
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
|
||||
}
|
||||
|
||||
try {
|
||||
Storage::put($dirname.$file_name, $uploaded_file);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug($e);
|
||||
}
|
||||
|
||||
return $file_name;
|
||||
}
|
||||
|
||||
public function handleSVG($file)
|
||||
{
|
||||
$sanitizer = new Sanitizer();
|
||||
$dirtySVG = file_get_contents($file->getRealPath());
|
||||
return $sanitizer->sanitize($dirtySVG);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the validation error messages that apply to the request, but
|
||||
* replace the attribute name with the name of the file that was attempted and failed
|
||||
* to make it clearer to the user which file is the bad one.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
if ($this->file) {
|
||||
for ($i = 0; $i < count($this->file); $i++) {
|
||||
$attributes['file.'.$i] = $this->file[$i]->getClientOriginalName();
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,7 @@ class AssetMaintenancesTransformer
|
||||
'name'=> e($assetmaintenance->asset->defaultLoc->name),
|
||||
] : null,
|
||||
'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedownInline($assetmaintenance->notes) : null,
|
||||
'supplier' => ($assetmaintenance->supplier) ? [
|
||||
'id' => $assetmaintenance->supplier->id,
|
||||
'name'=> e($assetmaintenance->supplier->name)
|
||||
] : null,
|
||||
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
|
||||
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
||||
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
||||
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
|
||||
|
||||
@@ -80,7 +80,6 @@ class AssetsTransformer
|
||||
'qr' => ($setting->qr_code=='1') ? config('app.url').'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png' : null,
|
||||
'alt_barcode' => ($setting->alt_barcode_enabled=='1') ? config('app.url').'/uploads/barcodes/'.str_slug($setting->alt_barcode).'-'.str_slug($asset->asset_tag).'.png' : null,
|
||||
'assigned_to' => $this->transformAssignedTo($asset),
|
||||
'jobtitle' => $asset->assigned ? e($asset->assigned->jobtitle) : null,
|
||||
'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months.' '.trans('admin/hardware/form.months')) : null,
|
||||
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
|
||||
'created_by' => ($asset->adminuser) ? [
|
||||
@@ -102,7 +101,7 @@ class AssetsTransformer
|
||||
'checkout_counter' => (int) $asset->checkout_counter,
|
||||
'requests_counter' => (int) $asset->requests_counter,
|
||||
'user_can_checkout' => (bool) $asset->availableForCheckout(),
|
||||
'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
|
||||
'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()),
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ namespace App\Http\Transformers;
|
||||
|
||||
class DatatablesTransformer
|
||||
{
|
||||
|
||||
/**
|
||||
* Transform data for bootstrap tables and API responses for lists of things
|
||||
**/
|
||||
public function transformDatatables($objects, $total = null)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
@@ -15,15 +11,4 @@ class DatatablesTransformer
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform data for returning the status of items within a bulk action
|
||||
**/
|
||||
public function transformBulkResponseWithStatusAndObjects($objects, $total)
|
||||
{
|
||||
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
|
||||
$objects_array['rows'] = $objects;
|
||||
|
||||
return $objects_array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,6 @@ class LocationsTransformer
|
||||
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($location->notes),
|
||||
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
|
||||
'created_by' => $location->adminuser ? [
|
||||
'id' => (int) $location->adminuser->id,
|
||||
'name'=> e($location->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
|
||||
'parent' => ($location->parent) ? [
|
||||
'id' => (int) $location->parent->id,
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ProfileTransformer
|
||||
{
|
||||
public function transformFiles(Collection $files, $total)
|
||||
{
|
||||
$array = [];
|
||||
foreach ($files as $file) {
|
||||
$array[] = self::transformFile($file);
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
|
||||
public function transformFile(Actionlog $file)
|
||||
{
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'icon' => Helper::filetype_icon($file->filename),
|
||||
'item' => ($file->item) ? [
|
||||
'name' => ($file->itemType()=='user') ? e($file->item->getFullNameAttribute()) : e($file->item->getDisplayNameAttribute()),
|
||||
'type' => e($file->itemType()),
|
||||
] : null,
|
||||
'filename' => e($file->filename),
|
||||
'signature_file' => ($file->accept_signature) ? route('profile.signature.view', ['filename' => $file->accept_signature ]) : null,
|
||||
'note' => e($file->note),
|
||||
'url' => route('profile.storedeula.download', ['filename' => $file->filename]),
|
||||
'file' => route('profile.storedeula.download', ['filename' => $file->filename]),
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,10 +45,6 @@ class SuppliersTransformer
|
||||
'components_count' => (int) $supplier->components_count,
|
||||
'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedownInline($supplier->notes) : null,
|
||||
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
|
||||
'created_by' => $supplier->adminuser ? [
|
||||
'id' => (int) $supplier->adminuser->id,
|
||||
'name'=> e($supplier->adminuser->present()->fullName),
|
||||
]: null,
|
||||
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
|
||||
|
||||
];
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Http\Transformers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Actionlog;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UploadedFilesTransformer
|
||||
@@ -26,26 +26,23 @@ class UploadedFilesTransformer
|
||||
{
|
||||
$snipeModel = $file->item_type;
|
||||
|
||||
|
||||
// This will be used later as we extend out this transformer to handle more types of uploads
|
||||
if ($file->item_type == Asset::class) {
|
||||
$file_url = route('show/assetfile', [$file->item_id, $file->id]);
|
||||
}
|
||||
|
||||
$array = [
|
||||
'id' => (int) $file->id,
|
||||
'icon' => Helper::filetype_icon($file->filename),
|
||||
'name' => e($file->filename),
|
||||
'item' => ($file->item_type) ? [
|
||||
'id' => (int) $file->item_id,
|
||||
'type' => strtolower(class_basename($file->item_type)),
|
||||
] : null,
|
||||
'filename' => e($file->filename),
|
||||
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
|
||||
'url' => $file->uploads_file_url(),
|
||||
'note' => ($file->note) ? e($file->note) : null,
|
||||
'url' => $file_url,
|
||||
'created_by' => ($file->adminuser) ? [
|
||||
'id' => (int) $file->adminuser->id,
|
||||
'name'=> e($file->adminuser->present()->fullName),
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
|
||||
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
|
||||
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
|
||||
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
@@ -56,5 +53,4 @@ class UploadedFilesTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,6 @@ class UsersTransformer
|
||||
'id' => (int) $user->department->id,
|
||||
'name'=> e($user->department->name),
|
||||
] : null,
|
||||
'department_manager' => ($user->department?->manager) ? [
|
||||
'id' => (int) $user->department->manager->id,
|
||||
'name'=> e($user->department->manager->full_name),
|
||||
] : null,
|
||||
'location' => ($user->userloc) ? [
|
||||
'id' => (int) $user->userloc->id,
|
||||
'name'=> e($user->userloc->name),
|
||||
|
||||
@@ -80,16 +80,7 @@ class AssetImporter extends ItemImporter
|
||||
$asset_tag = Asset::autoincrement_asset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($this->findCsvMatch($row, 'id')!='') {
|
||||
// Override asset if an ID was given
|
||||
\Log::debug('Finding asset by ID: '.$this->findCsvMatch($row, 'id'));
|
||||
$asset = Asset::find($this->findCsvMatch($row, 'id'));
|
||||
} else {
|
||||
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
|
||||
}
|
||||
|
||||
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
|
||||
if ($asset) {
|
||||
if (! $this->updating) {
|
||||
$exists_error = trans('general.import_asset_tag_exists', ['asset_tag' => $asset_tag]);
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
|
||||
* Importer\Importer.php. [ALG]
|
||||
*
|
||||
* Class CategoryImporter
|
||||
*/
|
||||
class CategoryImporter extends ItemImporter
|
||||
{
|
||||
protected $categories;
|
||||
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row);
|
||||
$this->createCategoryIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a category if a duplicate does not exist.
|
||||
* @todo Investigate how this should interact with Importer::createCategoryIfNotExists
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 6.1.0
|
||||
* @param array $row
|
||||
*/
|
||||
public function createCategoryIfNotExists(array $row)
|
||||
{
|
||||
|
||||
$editingCategory = false;
|
||||
|
||||
$category = Category::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
|
||||
|
||||
if ($this->findCsvMatch($row, 'id')!='') {
|
||||
// Override category if an ID was given
|
||||
\Log::debug('Finding category by ID: '.$this->findCsvMatch($row, 'id'));
|
||||
$category = Category::find($this->findCsvMatch($row, 'id'));
|
||||
}
|
||||
|
||||
|
||||
if ($category) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Category '.$this->item['name'].' already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('Updating Category');
|
||||
$editingCategory = true;
|
||||
} else {
|
||||
$this->log('No Matching Category, Create a new one');
|
||||
$category = new Category;
|
||||
$category->created_by = auth()->id();
|
||||
}
|
||||
|
||||
// Pull the records from the CSV to determine their values
|
||||
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
|
||||
$this->item['eula_text'] = trim($this->findCsvMatch($row, 'eula_text'));
|
||||
$this->item['category_type'] = trim(strtolower($this->findCsvMatch($row, 'category_type')));
|
||||
$this->item['use_default_eula'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'use_default_eula'))) == 1) ? 1 : 0;
|
||||
$this->item['require_acceptance'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'require_acceptance'))) == 1) ? 1 : 0;
|
||||
$this->item['checkin_email'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'checkin_email'))) == 1) ? 1 : 0;
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
Log::debug(print_r($this->item, true));
|
||||
|
||||
|
||||
if ($editingCategory) {
|
||||
Log::debug('Updating existing category');
|
||||
$category->update($this->sanitizeItemForUpdating($category));
|
||||
} else {
|
||||
Log::debug('Creating category');
|
||||
$category->fill($this->sanitizeItemForStoring($category));
|
||||
}
|
||||
|
||||
if ($category->save()) {
|
||||
$this->log('Category '.$category->name.' created or updated from CSV import');
|
||||
return $category;
|
||||
|
||||
} else {
|
||||
Log::debug($category->getErrors());
|
||||
$this->logError($category, 'Category "'.$this->item['name'].'"');
|
||||
return $category->errors;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,6 @@ abstract class Importer
|
||||
'department' => 'department',
|
||||
'manager_name' => 'manager full name',
|
||||
'manager_username' => 'manager username',
|
||||
'manager_employee_num' => 'manager employee number',
|
||||
'min_amt' => 'minimum quantity',
|
||||
'remote' => 'remote',
|
||||
'vip' => 'vip',
|
||||
|
||||
@@ -110,7 +110,7 @@ class ItemImporter extends Importer
|
||||
protected function determineCheckout($row)
|
||||
{
|
||||
// Locations don't get checked out to anyone/anything
|
||||
if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class) || (get_class($this) == SupplierImporter::class) || (get_class($this) == ManufacturerImporter::class) || (get_class($this) == CategoryImporter::class)) {
|
||||
if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -353,27 +353,16 @@ class ItemImporter extends Importer
|
||||
* @param $user_manager string
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function fetchManager($user_manager_username = null, $user_manager_employee_num = null, $user_manager_first_name = null, $user_manager_last_name = null)
|
||||
public function fetchManager($user_manager_first_name, $user_manager_last_name)
|
||||
{
|
||||
if ($user_manager_username!='') {
|
||||
$manager = User::where('username', '=', $user_manager_username)->first();
|
||||
$this->log('Checking on username '.$user_manager_username);
|
||||
} elseif ($user_manager_employee_num!='') {
|
||||
$manager = User::where('employee_num', '=', $user_manager_employee_num)->first();
|
||||
$this->log('Checking on employee_num '.$user_manager_employee_num);
|
||||
} else {
|
||||
$manager = User::where('first_name', '=', $user_manager_first_name)
|
||||
->where('last_name', '=', $user_manager_last_name)->first();
|
||||
$this->log('Checking on full name');
|
||||
}
|
||||
|
||||
$manager = User::where('first_name', '=', $user_manager_first_name)
|
||||
->where('last_name', '=', $user_manager_last_name)->first();
|
||||
if ($manager) {
|
||||
$this->log('A matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' already exists');
|
||||
|
||||
return $manager->id;
|
||||
}
|
||||
|
||||
$this->log('No matching Manager found. If their user account is being created through this import, you should re-process this file again. ');
|
||||
$this->log('No matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' found. If their user account is being created through this import, you should re-process this file again. ');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
|
||||
* Importer\Importer.php. [ALG]
|
||||
*
|
||||
* Class ManufacturerImporter
|
||||
*/
|
||||
class ManufacturerImporter extends ItemImporter
|
||||
{
|
||||
protected $manufacturers;
|
||||
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row);
|
||||
$this->createManufacturerIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a supplier if a duplicate does not exist.
|
||||
* @todo Investigate how this should interact with Importer::createManufacturerIfNotExists
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 6.1.0
|
||||
* @param array $row
|
||||
*/
|
||||
public function createManufacturerIfNotExists(array $row)
|
||||
{
|
||||
|
||||
$editingManufacturer = false;
|
||||
|
||||
$supplier = Manufacturer::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
|
||||
|
||||
if ($this->findCsvMatch($row, 'id')!='') {
|
||||
// Override supplier if an ID was given
|
||||
\Log::debug('Finding supplier by ID: '.$this->findCsvMatch($row, 'id'));
|
||||
$supplier = Manufacturer::find($this->findCsvMatch($row, 'id'));
|
||||
}
|
||||
|
||||
|
||||
if ($supplier) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Manufacturer '.$this->item['name'].' already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('Updating Manufacturer');
|
||||
$editingManufacturer = true;
|
||||
} else {
|
||||
$this->log('No Matching Manufacturer, Create a new one');
|
||||
$supplier = new Manufacturer;
|
||||
$supplier->created_by = auth()->id();
|
||||
}
|
||||
|
||||
// Pull the records from the CSV to determine their values
|
||||
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
|
||||
$this->item['support_phone'] = trim($this->findCsvMatch($row, 'support_phone'));
|
||||
$this->item['fax'] = trim($this->findCsvMatch($row, 'fax'));
|
||||
$this->item['support_email'] = trim($this->findCsvMatch($row, 'support_email'));
|
||||
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
|
||||
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
|
||||
$this->item['support_url'] = trim($this->findCsvMatch($row, 'support_url'));
|
||||
$this->item['warranty_lookup_url'] = trim($this->findCsvMatch($row, 'warranty_lookup_url'));
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
Log::debug(print_r($this->item, true));
|
||||
|
||||
|
||||
if ($editingManufacturer) {
|
||||
Log::debug('Updating existing supplier');
|
||||
$supplier->update($this->sanitizeItemForUpdating($supplier));
|
||||
} else {
|
||||
Log::debug('Creating supplier');
|
||||
$supplier->fill($this->sanitizeItemForStoring($supplier));
|
||||
}
|
||||
|
||||
if ($supplier->save()) {
|
||||
$this->log('Manufacturer '.$supplier->name.' created or updated from CSV import');
|
||||
return $supplier;
|
||||
|
||||
} else {
|
||||
Log::debug($supplier->getErrors());
|
||||
$this->logError($supplier, 'Manufacturer "'.$this->item['name'].'"');
|
||||
return $supplier->errors;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
|
||||
* Importer\Importer.php. [ALG]
|
||||
*
|
||||
* Class SupplierImporter
|
||||
*/
|
||||
class SupplierImporter extends ItemImporter
|
||||
{
|
||||
protected $suppliers;
|
||||
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row);
|
||||
$this->createSupplierIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a supplier if a duplicate does not exist.
|
||||
* @todo Investigate how this should interact with Importer::createSupplierIfNotExists
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 6.1.0
|
||||
* @param array $row
|
||||
*/
|
||||
public function createSupplierIfNotExists(array $row)
|
||||
{
|
||||
|
||||
$editingSupplier = false;
|
||||
|
||||
$supplier = Supplier::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
|
||||
|
||||
if ($this->findCsvMatch($row, 'id')!='') {
|
||||
// Override supplier if an ID was given
|
||||
\Log::debug('Finding supplier by ID: '.$this->findCsvMatch($row, 'id'));
|
||||
$supplier = Supplier::find($this->findCsvMatch($row, 'id'));
|
||||
}
|
||||
|
||||
|
||||
if ($supplier) {
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Supplier '.$this->item['name'].' already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('Updating Supplier');
|
||||
$editingSupplier = true;
|
||||
} else {
|
||||
$this->log('No Matching Supplier, Create a new one');
|
||||
$supplier = new Supplier;
|
||||
$supplier->created_by = auth()->id();
|
||||
}
|
||||
|
||||
// Pull the records from the CSV to determine their values
|
||||
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
|
||||
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));
|
||||
$this->item['address2'] = trim($this->findCsvMatch($row, 'address2'));
|
||||
$this->item['city'] = trim($this->findCsvMatch($row, 'city'));
|
||||
$this->item['state'] = trim($this->findCsvMatch($row, 'state'));
|
||||
$this->item['country'] = trim($this->findCsvMatch($row, 'country'));
|
||||
$this->item['zip'] = trim($this->findCsvMatch($row, 'zip'));
|
||||
$this->item['phone'] = trim($this->findCsvMatch($row, 'phone'));
|
||||
$this->item['fax'] = trim($this->findCsvMatch($row, 'fax'));
|
||||
$this->item['email'] = trim($this->findCsvMatch($row, 'email'));
|
||||
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
|
||||
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
|
||||
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
|
||||
|
||||
|
||||
Log::debug('Item array is: ');
|
||||
Log::debug(print_r($this->item, true));
|
||||
|
||||
|
||||
if ($editingSupplier) {
|
||||
Log::debug('Updating existing supplier');
|
||||
$supplier->update($this->sanitizeItemForUpdating($supplier));
|
||||
} else {
|
||||
Log::debug('Creating supplier');
|
||||
$supplier->fill($this->sanitizeItemForStoring($supplier));
|
||||
}
|
||||
|
||||
if ($supplier->save()) {
|
||||
$this->log('Supplier '.$supplier->name.' created or updated from CSV import');
|
||||
return $supplier;
|
||||
|
||||
} else {
|
||||
Log::debug($supplier->getErrors());
|
||||
$this->logError($supplier, 'Supplier "'.$this->item['name'].'"');
|
||||
return $supplier->errors;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class UserImporter extends ItemImporter
|
||||
$this->item['activated'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'activated'))) == 1) ? '1' : 0;
|
||||
$this->item['employee_num'] = trim($this->findCsvMatch($row, 'employee_num'));
|
||||
$this->item['department_id'] = trim($this->createOrFetchDepartment(trim($this->findCsvMatch($row, 'department'))));
|
||||
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_username')), trim($this->findCsvMatch($row, 'manager_employee_num')), trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
|
||||
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
|
||||
$this->item['remote'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'remote'))) == 1 ) ? '1' : 0;
|
||||
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
|
||||
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;
|
||||
|
||||
@@ -12,7 +12,6 @@ use App\Mail\CheckoutConsumableMail;
|
||||
use App\Mail\CheckoutLicenseMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Category;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
@@ -70,16 +69,16 @@ class CheckoutableListener
|
||||
return;
|
||||
}
|
||||
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
|
||||
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($event->checkoutable);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress($acceptance);
|
||||
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
|
||||
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
|
||||
|
||||
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
$acceptance = $this->getCheckoutAcceptance($event);
|
||||
|
||||
if ($shouldSendEmailToUser || $shouldSendEmailToAlertAddress) {
|
||||
$mailable = $this->getCheckoutMailType($event, $acceptance);
|
||||
$notifiable = $this->getNotifiableUser($event);
|
||||
@@ -228,7 +227,6 @@ class CheckoutableListener
|
||||
if ($checkedOutToType != "App\Models\User") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$event->checkoutable->requireAcceptance()) {
|
||||
return null;
|
||||
}
|
||||
@@ -236,13 +234,6 @@ class CheckoutableListener
|
||||
$acceptance = new CheckoutAcceptance;
|
||||
$acceptance->checkoutable()->associate($event->checkoutable);
|
||||
$acceptance->assignedTo()->associate($event->checkedOutTo);
|
||||
|
||||
$category = $this->getCategoryFromCheckoutable($event->checkoutable);
|
||||
|
||||
if ($category?->alert_on_response) {
|
||||
$acceptance->alert_on_response_id = auth()->id();
|
||||
}
|
||||
|
||||
$acceptance->save();
|
||||
|
||||
return $acceptance;
|
||||
@@ -369,6 +360,23 @@ class CheckoutableListener
|
||||
return in_array(get_class($checkoutable), $this->skipNotificationsFor);
|
||||
}
|
||||
|
||||
private function shouldSendEmailNotifications(Model $checkoutable): bool
|
||||
{
|
||||
//runs a check if the category wants to send checkin/checkout emails to users
|
||||
$category = match (true) {
|
||||
$checkoutable instanceof Asset => $checkoutable->model->category,
|
||||
$checkoutable instanceof Accessory,
|
||||
$checkoutable instanceof Consumable => $checkoutable->category,
|
||||
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (!$category?->checkin_email) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function shouldSendWebhookNotification(): bool
|
||||
{
|
||||
return Setting::getSettings() && Setting::getSettings()->webhook_endpoint;
|
||||
@@ -411,19 +419,9 @@ class CheckoutableListener
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldSendEmailToAlertAddress($acceptance = null): bool
|
||||
private function shouldSendEmailToAlertAddress(): bool
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
if (!$setting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($acceptance) && !$setting->admin_cc_always) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $setting->admin_cc_email;
|
||||
return Setting::getSettings() && Setting::getSettings()->admin_cc_email;
|
||||
}
|
||||
|
||||
private function getFormattedAlertAddresses(): array
|
||||
@@ -463,14 +461,4 @@ class CheckoutableListener
|
||||
|
||||
return array($to, $cc);
|
||||
}
|
||||
|
||||
private function getCategoryFromCheckoutable(Model $checkoutable): ?Category
|
||||
{
|
||||
return match (true) {
|
||||
$checkoutable instanceof Asset => $checkoutable->model->category,
|
||||
$checkoutable instanceof Accessory,
|
||||
$checkoutable instanceof Consumable => $checkoutable->category,
|
||||
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ use Livewire\Component;
|
||||
|
||||
class CategoryEditForm extends Component
|
||||
{
|
||||
public bool $alertOnResponse;
|
||||
|
||||
public $defaultEulaText;
|
||||
|
||||
public $eulaText;
|
||||
|
||||
@@ -35,14 +35,10 @@ class Importer extends Component
|
||||
public $accessories_fields;
|
||||
public $assets_fields;
|
||||
public $users_fields;
|
||||
public $assetmodels_fields;
|
||||
public $suppliers_fields;
|
||||
public $licenses_fields;
|
||||
public $locations_fields;
|
||||
public $consumables_fields;
|
||||
public $components_fields;
|
||||
public $manufacturers_fields;
|
||||
public $categories_fields;
|
||||
public $aliases_fields;
|
||||
|
||||
protected $rules = [
|
||||
@@ -89,6 +85,9 @@ class Importer extends Component
|
||||
case 'component':
|
||||
$results = $this->components_fields;
|
||||
break;
|
||||
case 'consumable':
|
||||
$results = $this->consumables_fields;
|
||||
break;
|
||||
case 'license':
|
||||
$results = $this->licenses_fields;
|
||||
break;
|
||||
@@ -98,14 +97,8 @@ class Importer extends Component
|
||||
case 'location':
|
||||
$results = $this->locations_fields;
|
||||
break;
|
||||
case 'supplier':
|
||||
$results = $this->suppliers_fields;
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$results = $this->manufacturers_fields;
|
||||
break;
|
||||
case 'category':
|
||||
$results = $this->categories_fields;
|
||||
case 'user':
|
||||
$results = $this->users_fields;
|
||||
break;
|
||||
default:
|
||||
$results = [];
|
||||
@@ -135,7 +128,7 @@ class Importer extends Component
|
||||
//yes, this key *is* valid. Continue on to the next field.
|
||||
continue;
|
||||
} else {
|
||||
//no, this key is *INVALID* for this import type. Better set it to null,
|
||||
//no, this key is *INVALID* for this import type. Better set it to null
|
||||
// and we'll hope that the $aliases_fields or something else picks it up.
|
||||
$this->field_map[$i] = null; // fingers crossed! But it's not likely, tbh.
|
||||
} // TODO - strictly speaking, this isn't necessary here I don't think.
|
||||
@@ -156,7 +149,7 @@ class Importer extends Component
|
||||
// in "Accessories"!)
|
||||
if (array_key_exists($key, $this->columnOptions[$type])) {
|
||||
$this->field_map[$i] = $key;
|
||||
continue 3; // bust out of both of these loops and the surrounding one - e.g. move on to the next header
|
||||
continue 3; // bust out of both of these loops; as well as the surrounding one - e.g. move on to the next header
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,9 +171,6 @@ class Importer extends Component
|
||||
'license' => trans('general.licenses'),
|
||||
'location' => trans('general.locations'),
|
||||
'user' => trans('general.users'),
|
||||
'supplier' => trans('general.suppliers'),
|
||||
'manufacturer' => trans('general.manufacturers'),
|
||||
'category' => trans('general.categories'),
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -203,7 +193,6 @@ class Importer extends Component
|
||||
];
|
||||
|
||||
$this->assets_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'asset_eol_date' => trans('admin/hardware/form.eol_date'),
|
||||
'asset_model' => trans('general.model_name'),
|
||||
'asset_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/general.asset')]),
|
||||
@@ -330,8 +319,6 @@ class Importer extends Component
|
||||
'location' => trans('general.location'),
|
||||
'manager_first_name' => trans('general.importer.manager_first_name'),
|
||||
'manager_last_name' => trans('general.importer.manager_last_name'),
|
||||
'manager_employee_num' => trans('general.importer.manager_employee_num'),
|
||||
'manager_username' => trans('general.importer.manager_username'),
|
||||
'notes' => trans('general.notes'),
|
||||
'phone_number' => trans('admin/users/table.phone'),
|
||||
'remote' => trans('admin/users/general.remote'),
|
||||
@@ -345,7 +332,6 @@ class Importer extends Component
|
||||
|
||||
$this->locations_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'name' => trans('general.name'),
|
||||
'address' => trans('general.address'),
|
||||
'address2' => trans('general.importer.address2'),
|
||||
'city' => trans('general.city'),
|
||||
@@ -354,52 +340,13 @@ class Importer extends Component
|
||||
'ldap_ou' => trans('admin/locations/table.ldap_ou'),
|
||||
'manager' => trans('general.importer.manager_full_name'),
|
||||
'manager_username' => trans('general.importer.manager_username'),
|
||||
'name' => trans('general.item_name_var', ['item' => trans('general.location')]),
|
||||
'notes' => trans('general.notes'),
|
||||
'parent_location' => trans('admin/locations/table.parent'),
|
||||
'state' => trans('general.state'),
|
||||
'zip' => trans('general.zip'),
|
||||
];
|
||||
|
||||
$this->suppliers_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'name' => trans('general.name'),
|
||||
'address' => trans('general.address'),
|
||||
'address2' => trans('general.importer.address2'),
|
||||
'city' => trans('general.city'),
|
||||
'notes' => trans('general.notes'),
|
||||
'state' => trans('general.state'),
|
||||
'zip' => trans('general.zip'),
|
||||
'phone' => trans('general.phone'),
|
||||
'fax' => trans('general.fax'),
|
||||
'url' => trans('general.url'),
|
||||
'contact' => trans('general.contact'),
|
||||
'email' => trans('general.email'),
|
||||
];
|
||||
|
||||
$this->manufacturers_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'name' => trans('general.name'),
|
||||
'notes' => trans('general.notes'),
|
||||
'support_phone' => trans('admin/manufacturers/table.support_phone'),
|
||||
'support_url' => trans('admin/manufacturers/table.support_url'),
|
||||
'support_email' => trans('admin/manufacturers/table.support_email'),
|
||||
'warranty_lookup_url' => trans('admin/manufacturers/table.warranty_lookup_url'),
|
||||
'url' => trans('general.url'),
|
||||
];
|
||||
|
||||
$this->categories_fields = [
|
||||
'id' => trans('general.id'),
|
||||
'name' => trans('general.name'),
|
||||
'notes' => trans('general.notes'),
|
||||
'category_type' => trans('admin/categories/general.import_category_type'),
|
||||
'eula_text' => trans('admin/categories/general.import_eula_text'),
|
||||
'use_default_eula' => trans('admin/categories/general.use_default_eula_column'),
|
||||
'require_acceptance' => trans('admin/categories/general.import_require_acceptance'),
|
||||
'checkin_email' => trans('admin/categories/general.import_checkin_email'),
|
||||
];
|
||||
|
||||
|
||||
|
||||
$this->assetmodels_fields = [
|
||||
'category' => trans('general.category'),
|
||||
'eol' => trans('general.eol'),
|
||||
@@ -424,8 +371,6 @@ class Importer extends Component
|
||||
'consumable name',
|
||||
'component name',
|
||||
'name',
|
||||
'supplier name',
|
||||
'location name',
|
||||
],
|
||||
'item_no' => [
|
||||
'item number',
|
||||
|
||||
@@ -10,16 +10,15 @@ class LocationScopeCheck extends Component
|
||||
{
|
||||
public $mismatched = [];
|
||||
public $setting;
|
||||
public $is_tested = false;
|
||||
|
||||
public function check_locations()
|
||||
{
|
||||
$this->mismatched = Helper::test_locations_fmcs(false);
|
||||
$this->is_tested = true;
|
||||
}
|
||||
|
||||
public function mount() {
|
||||
$this->setting = Setting::getSettings();
|
||||
$this->mismatched = Helper::test_locations_fmcs(false);
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CheckoutAcceptanceResponseMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public CheckoutAcceptance $acceptance;
|
||||
public User $recipient;
|
||||
public bool $wasAccepted;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(CheckoutAcceptance $acceptance, User $recipient, bool $wasAccepted)
|
||||
{
|
||||
$this->acceptance = $acceptance;
|
||||
$this->recipient = $recipient;
|
||||
$this->wasAccepted = $wasAccepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$subject = $this->wasAccepted
|
||||
? trans('mail.initiated_accepted')
|
||||
: trans('mail.initiated_declined');
|
||||
|
||||
return new Envelope(
|
||||
subject: $subject,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mail.markdown.checkout-acceptance-response',
|
||||
with: [
|
||||
'assignedTo' => $this->acceptance->assignedTo,
|
||||
'introduction' => $this->introduction(),
|
||||
'item' => $this->acceptance->checkoutable,
|
||||
'note' => $this->acceptance->note,
|
||||
'recipient' => $this->recipient,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function introduction(): string
|
||||
{
|
||||
return $this->wasAccepted
|
||||
? trans('mail.following_accepted')
|
||||
: trans('mail.following_declined');
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -23,7 +22,6 @@ class Accessory extends SnipeModel
|
||||
|
||||
protected $presenter = \App\Presenters\AccessoryPresenter::class;
|
||||
use CompanyableTrait;
|
||||
use HasUploads;
|
||||
use Loggable, Presentable;
|
||||
use SoftDeletes;
|
||||
|
||||
@@ -104,6 +102,24 @@ class Accessory extends SnipeModel
|
||||
];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the accessories -> action logs -> uploads relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v6.1.13]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
|
||||
->where('item_type', '=', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the accessory -> supplier relationship
|
||||
*
|
||||
@@ -292,7 +308,7 @@ class Accessory extends SnipeModel
|
||||
*/
|
||||
public function checkin_email()
|
||||
{
|
||||
return $this->category?->checkin_email;
|
||||
return $this->category->checkin_email;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,8 +69,8 @@ class Actionlog extends SnipeModel
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'company' => ['name'],
|
||||
'adminuser' => ['first_name','last_name','username', 'email', 'employee_num'],
|
||||
'user' => ['first_name','last_name','username', 'email', 'employee_num'],
|
||||
'adminuser' => ['first_name','last_name','username', 'email'],
|
||||
'user' => ['first_name','last_name','username', 'email'],
|
||||
'assets' => ['asset_tag','name', 'serial', 'order_number', 'notes', 'purchase_date'],
|
||||
'assets.model' => ['name', 'model_number', 'eol', 'notes'],
|
||||
'assets.model.category' => ['name', 'notes'],
|
||||
@@ -113,11 +113,6 @@ class Actionlog extends SnipeModel
|
||||
} elseif (auth()->user() && auth()->user()->company) {
|
||||
$actionlog->company_id = auth()->user()->company_id;
|
||||
}
|
||||
|
||||
if ($actionlog->action_date == '') {
|
||||
$actionlog->action_date = Carbon::now();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -250,8 +245,8 @@ class Actionlog extends SnipeModel
|
||||
public function uploads()
|
||||
{
|
||||
return $this->morphTo('item')
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->withTrashed();
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +271,7 @@ class Actionlog extends SnipeModel
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')
|
||||
->withTrashed();
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +376,7 @@ class Actionlog extends SnipeModel
|
||||
if ($this->created_at > $override_default_next) {
|
||||
$next_audit_days = '-'.$next_audit_days;
|
||||
}
|
||||
|
||||
|
||||
return $next_audit_days;
|
||||
}
|
||||
|
||||
@@ -413,10 +408,10 @@ class Actionlog extends SnipeModel
|
||||
public function getListingOfActionLogsChronologicalOrder()
|
||||
{
|
||||
return $this->all()
|
||||
->where('action_type', '!=', 'uploaded')
|
||||
->orderBy('item_id', 'asc')
|
||||
->orderBy('created_at', 'asc')
|
||||
->get();
|
||||
->where('action_type', '!=', 'uploaded')
|
||||
->orderBy('item_id', 'asc')
|
||||
->orderBy('created_at', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,7 +434,7 @@ class Actionlog extends SnipeModel
|
||||
return 'api';
|
||||
}
|
||||
|
||||
// This is probably NOT an API call
|
||||
// This is probably NOT an API call
|
||||
if (request()->filled('_token')) {
|
||||
return 'gui';
|
||||
}
|
||||
@@ -449,62 +444,6 @@ class Actionlog extends SnipeModel
|
||||
|
||||
}
|
||||
|
||||
public function uploads_file_url()
|
||||
{
|
||||
|
||||
switch ($this->item_type) {
|
||||
case Accessory::class:
|
||||
return route('show.accessoryfile', [$this->item_id, $this->id]);
|
||||
case Asset::class:
|
||||
return route('show/assetfile', [$this->item_id, $this->id]);
|
||||
case AssetModel::class:
|
||||
return route('show/modelfile', [$this->item_id, $this->id]);
|
||||
case Consumable::class:
|
||||
return route('show/locationsfile', [$this->item_id, $this->id]);
|
||||
case Component::class:
|
||||
return route('show.componentfile', [$this->item_id, $this->id]);
|
||||
case License::class:
|
||||
return route('show.licensefile', [$this->item_id, $this->id]);
|
||||
case Location::class:
|
||||
return route('show/locationsfile', [$this->item_id, $this->id]);
|
||||
case User::class:
|
||||
return route('show/userfile', [$this->item_id, $this->id]);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function uploads_file_path()
|
||||
{
|
||||
|
||||
switch ($this->item_type) {
|
||||
case Accessory::class:
|
||||
return 'private_uploads/accessories/'.$this->filename;
|
||||
case Asset::class:
|
||||
return 'private_uploads/assets/'.$this->filename;
|
||||
case AssetModel::class:
|
||||
return 'private_uploads/assetmodels/'.$this->filename;
|
||||
case Consumable::class:
|
||||
return 'private_uploads/consumables/'.$this->filename;
|
||||
case Component::class:
|
||||
return 'private_uploads/components/'.$this->filename;
|
||||
case License::class:
|
||||
return 'private_uploads/licenses/'.$this->filename;
|
||||
case Location::class:
|
||||
return 'private_uploads/locations/'.$this->filename;
|
||||
case User::class:
|
||||
return 'private_uploads/users/'.$this->filename;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Manually sets $this->source for determineActionSource()
|
||||
public function setActionSource($source = null): void
|
||||
{
|
||||
@@ -515,4 +454,4 @@ class Actionlog extends SnipeModel
|
||||
{
|
||||
return $query->leftJoin('users as admin_sort', 'action_logs.created_by', '=', 'admin_sort.id')->select('action_logs.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,19 @@ use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use App\Presenters\AssetPresenter;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Model for Assets.
|
||||
@@ -31,7 +33,6 @@ class Asset extends Depreciable
|
||||
protected $with = ['model', 'adminuser'];
|
||||
|
||||
use CompanyableTrait;
|
||||
use HasUploads;
|
||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||
|
||||
public const LOCATION = 'location';
|
||||
@@ -121,9 +122,9 @@ class Asset extends Depreciable
|
||||
'assigned_to' => ['nullable', 'integer', 'required_with:assigned_type'],
|
||||
'assigned_type' => ['nullable', 'required_with:assigned_to', 'in:'.User::class.",".Location::class.",".Asset::class],
|
||||
'requestable' => ['nullable', 'boolean'],
|
||||
'assigned_user' => ['integer', 'nullable', 'exists:users,id,deleted_at,NULL'],
|
||||
'assigned_location' => ['integer', 'nullable', 'exists:locations,id,deleted_at,NULL', 'fmcs_location'],
|
||||
'assigned_asset' => ['integer', 'nullable', 'exists:assets,id,deleted_at,NULL']
|
||||
'assigned_user' => ['nullable', 'exists:users,id,deleted_at,NULL'],
|
||||
'assigned_location' => ['nullable', 'exists:locations,id,deleted_at,NULL', 'fmcs_location'],
|
||||
'assigned_asset' => ['nullable', 'exists:assets,id,deleted_at,NULL']
|
||||
];
|
||||
|
||||
|
||||
@@ -471,10 +472,26 @@ class Asset extends Depreciable
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get uploads for this asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany('\App\Models\Actionlog', 'item_id')
|
||||
->where('item_type', '=', Asset::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the asset is checked out to a user
|
||||
*
|
||||
* Even though we allow for checkout to things beyond users
|
||||
* Even though we allow allow for checkout to things beyond users
|
||||
* this method is an easy way of seeing if we are checked out to a user.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -946,7 +963,6 @@ class Asset extends Depreciable
|
||||
return $this->model->category->require_acceptance;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1113,7 +1129,6 @@ class Asset extends Depreciable
|
||||
$query = $query
|
||||
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
|
||||
->orWhereMultipleColumns([
|
||||
@@ -1570,13 +1585,11 @@ class Asset extends Depreciable
|
||||
})->orWhere(function ($query) use ($search) {
|
||||
$query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$search.'%')
|
||||
->orWhereMultipleColumns([
|
||||
'assets_users.first_name',
|
||||
'assets_users.last_name',
|
||||
'assets_users.jobtitle',
|
||||
], $search)
|
||||
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
|
||||
->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
|
||||
})->orWhere('assets.name', 'LIKE', '%'.$search.'%')
|
||||
@@ -1959,19 +1972,6 @@ class Asset extends Depreciable
|
||||
return $query->leftJoin('suppliers as suppliers_assets', 'assets.supplier_id', '=', 'suppliers_assets.id')->orderBy('suppliers_assets.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on supplier name
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $order Order
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeOrderByJobTitle($query, $order)
|
||||
{
|
||||
return $query->leftJoin('users as users_sort', 'assets.assigned_to', '=', 'users_sort.id')->select('assets.*')->orderBy('users_sort.jobtitle', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on location ID
|
||||
*
|
||||
|
||||
@@ -26,12 +26,12 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
protected $table = 'asset_maintenances';
|
||||
protected $rules = [
|
||||
'asset_id' => 'required|integer',
|
||||
'supplier_id' => 'nullable|integer',
|
||||
'supplier_id' => 'required|integer',
|
||||
'asset_maintenance_type' => 'required',
|
||||
'title' => 'required|max:100',
|
||||
'is_warranty' => 'boolean',
|
||||
'start_date' => 'required|date_format:Y-m-d',
|
||||
'completion_date' => 'date_format:Y-m-d|nullable|after_or_equal:start_date',
|
||||
'completion_date' => 'date_format:Y-m-d|nullable',
|
||||
'notes' => 'string|nullable',
|
||||
'cost' => 'numeric|nullable',
|
||||
];
|
||||
@@ -166,7 +166,6 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
return $this->belongsTo(\App\Models\Asset::class, 'asset_id')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the admin who created the maintenance
|
||||
@@ -266,21 +265,6 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||
->orderBy('maintained_asset_status.name', $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 scopeOrderLocationName($query, $order)
|
||||
{
|
||||
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
|
||||
->leftjoin('locations as maintained_asset_location', 'maintained_asset_location.id', '=', 'maintained_asset.location_id')
|
||||
->orderBy('maintained_asset_location.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on the user that created it
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -25,7 +24,6 @@ class AssetModel extends SnipeModel
|
||||
use SoftDeletes;
|
||||
use Loggable, Requestable, Presentable;
|
||||
use TwoColumnUniqueUndeletedTrait;
|
||||
use HasUploads;
|
||||
|
||||
/**
|
||||
* Whether the model should inject its identifier to the unique
|
||||
@@ -211,6 +209,21 @@ class AssetModel extends SnipeModel
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uploads for this model
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany('\App\Models\Actionlog', 'item_id')
|
||||
->where('item_type', '=', AssetModel::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user who created the item
|
||||
|
||||
@@ -32,7 +32,6 @@ class Category extends SnipeModel
|
||||
protected $hidden = ['created_by', 'deleted_at'];
|
||||
|
||||
protected $casts = [
|
||||
'alert_on_response' => 'boolean',
|
||||
'created_by' => 'integer',
|
||||
];
|
||||
|
||||
@@ -70,7 +69,6 @@ class Category extends SnipeModel
|
||||
'eula_text',
|
||||
'name',
|
||||
'require_acceptance',
|
||||
'alert_on_response',
|
||||
'use_default_eula',
|
||||
'created_by',
|
||||
'notes',
|
||||
|
||||
@@ -15,7 +15,6 @@ class CheckoutAcceptance extends Model
|
||||
protected $casts = [
|
||||
'accepted_at' => 'datetime',
|
||||
'declined_at' => 'datetime',
|
||||
'alert_on_response_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -160,7 +160,7 @@ final class Company extends SnipeModel
|
||||
|
||||
|
||||
if (auth()->user()) {
|
||||
// Log::warning('Companyable is '.$companyable);
|
||||
Log::warning('Companyable is '.$companyable);
|
||||
$current_user_company_id = auth()->user()->company_id;
|
||||
$companyable_company_id = $companyable->company_id;
|
||||
return $current_user_company_id == null || $current_user_company_id == $companyable_company_id || auth()->user()->isSuperUser();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -21,7 +20,6 @@ class Component extends SnipeModel
|
||||
|
||||
protected $presenter = \App\Presenters\ComponentPresenter::class;
|
||||
use CompanyableTrait;
|
||||
use HasUploads;
|
||||
use Loggable, Presentable;
|
||||
use SoftDeletes;
|
||||
protected $casts = [
|
||||
@@ -115,6 +113,21 @@ class Component extends SnipeModel
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the components -> action logs -> uploads relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v6.1.13]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
|
||||
->where('item_type', '=', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -30,7 +29,6 @@ class Consumable extends SnipeModel
|
||||
use Loggable, Presentable;
|
||||
use SoftDeletes;
|
||||
use Acceptable;
|
||||
use HasUploads;
|
||||
|
||||
protected $table = 'consumables';
|
||||
protected $casts = [
|
||||
@@ -113,6 +111,21 @@ class Consumable extends SnipeModel
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the components -> action logs -> uploads relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v6.1.13]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(Actionlog::class, 'item_id')
|
||||
->where('item_type', '=', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -271,7 +284,7 @@ class Consumable extends SnipeModel
|
||||
*/
|
||||
public function checkin_email()
|
||||
{
|
||||
return $this->category?->checkin_email;
|
||||
return $this->category->checkin_email;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,8 +43,8 @@ class DefaultLabel extends RectangleSheet
|
||||
|
||||
$this->textSize = Helper::convertUnit($settings->labels_fontsize, 'pt', 'in');
|
||||
|
||||
$this->labelWidth = $this->setLabelWidth($settings);
|
||||
$this->labelHeight = $this->setLabelHeight($settings);
|
||||
$this->labelWidth = $settings->labels_width;
|
||||
$this->labelHeight = $settings->labels_height;
|
||||
|
||||
$this->labelSpacingH = $settings->labels_display_sgutter;
|
||||
$this->labelSpacingV = $settings->labels_display_bgutter;
|
||||
@@ -181,25 +181,6 @@ class DefaultLabel extends RectangleSheet
|
||||
}
|
||||
}
|
||||
|
||||
private function setLabelWidth(Setting $settings)
|
||||
{
|
||||
$labelWidth = $settings->labels_width;
|
||||
|
||||
if ($labelWidth == 0) {
|
||||
$labelWidth = 0.1;
|
||||
}
|
||||
|
||||
return $labelWidth;
|
||||
}
|
||||
|
||||
private function setLabelHeight(?Setting $settings)
|
||||
{
|
||||
$labelHeight = $settings->labels_height;
|
||||
|
||||
if ($labelHeight == 0) {
|
||||
$labelHeight = 0.1;
|
||||
}
|
||||
|
||||
return $labelHeight;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
abstract class Continuous_53mm extends GenericTape
|
||||
{
|
||||
protected const TAPE_WIDTH = 53.0;
|
||||
|
||||
// Minimum height for the label
|
||||
protected float $minHeight = 30.0;
|
||||
private float $tapeHeight;
|
||||
|
||||
/**
|
||||
* Constructor for 53mm tape
|
||||
*
|
||||
* Assumes tape is continuous, set to false and specify
|
||||
* $spacing in concrete classfor die-cut labels
|
||||
*
|
||||
*
|
||||
* @param float $height Height of the label in mm (default 60mm)
|
||||
* @param bool $continuous Whether the tape is continuous or pre-cut
|
||||
* @param float $spacing Spacing between labels for non-continuous tapes (in mm)
|
||||
*/
|
||||
public function __construct($height = 60.0, $continuous = true, $spacing = 0.0) {
|
||||
parent::__construct(self::TAPE_WIDTH, $height, $continuous, $spacing);
|
||||
$this->tapeHeight = $height;
|
||||
}
|
||||
|
||||
public function getBarcodeRatio() {
|
||||
return 0.9; // Barcode should use 90% of available width
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the required height for the content
|
||||
*
|
||||
* @param $record The record to calculate height for
|
||||
* @return float The calculated height in mm
|
||||
*/
|
||||
protected function calculateRequiredHeight($record) {
|
||||
$height = $this->marginTop + $this->marginBottom;
|
||||
|
||||
// Add title height if present
|
||||
if ($record->has('title') && $this->getSupportTitle()) {
|
||||
$height += $this->titleSize + $this->titleMargin;
|
||||
}
|
||||
|
||||
// Add barcode height if present
|
||||
if (($record->has('barcode2d') && $this->getSupport2DBarcode()) ||
|
||||
($record->has('barcode') && $this->getSupport1DBarcode())) {
|
||||
$pa = $this->getPrintableArea();
|
||||
$usableWidth = $pa->w;
|
||||
$barcodeSize = $usableWidth * $this->getBarcodeRatio();
|
||||
$height += $barcodeSize + $this->barcodeMargin;
|
||||
}
|
||||
|
||||
// Add fields height if present
|
||||
if ($record->has('fields') && $this->getSupportFields() > 0) {
|
||||
foreach ($record->get('fields') as $field) {
|
||||
$height += $this->labelSize + $this->labelMargin;
|
||||
$height += $this->fieldSize + $this->fieldMargin;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a small buffer to ensure everything fits
|
||||
$height += 2.0;
|
||||
|
||||
// Ensure minimum height
|
||||
return max($this->minHeight, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the writeAll method to support dynamic page sizes for continuous tapes
|
||||
*/
|
||||
public function writeAll($pdf, $data) {
|
||||
// Use auto-sizing for continuous tapes, fixed height for die-cut tapes
|
||||
if ($this->continuous) {
|
||||
$data->each(function ($record, $index) use ($pdf) {
|
||||
// Calculate the required height for this record
|
||||
$requiredHeight = $this->calculateRequiredHeight($record);
|
||||
|
||||
// Temporarily update the height property
|
||||
$originalHeight = $this->height;
|
||||
$this->height = $requiredHeight;
|
||||
|
||||
// Add a new page with the calculated dimensions
|
||||
$pdf->AddPage(
|
||||
$this->getOrientation(),
|
||||
[$this->getWidth(), $requiredHeight],
|
||||
false, // Don't reset page number
|
||||
false // Don't reset object ID
|
||||
);
|
||||
|
||||
// Write the content
|
||||
$this->write($pdf, $record);
|
||||
|
||||
// Restore the original height
|
||||
$this->height = $originalHeight;
|
||||
});
|
||||
} else {
|
||||
// Use the default implementation for non-continuous (die-cut) tapes
|
||||
parent::writeAll($pdf, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
class Continuous_53mm_A extends Continuous_53mm
|
||||
{
|
||||
|
||||
public function getUnit() { return 'mm'; }
|
||||
public function getSupportAssetTag() { return false; }
|
||||
public function getSupport1DBarcode() { return true; }
|
||||
public function getSupport2DBarcode() { return true; }
|
||||
public function getSupportFields() { return 5; }
|
||||
public function getSupportLogo() { return false; }
|
||||
public function getSupportTitle() { return true; }
|
||||
|
||||
public function preparePDF($pdf) {
|
||||
$pdf->SetAutoPageBreak(false);
|
||||
}
|
||||
|
||||
public function write($pdf, $record) {
|
||||
$pa = $this->getPrintableArea();
|
||||
|
||||
$currentX = $pa->x1;
|
||||
$currentY = $pa->y1;
|
||||
$usableWidth = $pa->w;
|
||||
$usableHeight = $pa->h;
|
||||
|
||||
if ($record->has('title')) {
|
||||
static::writeText(
|
||||
$pdf, $record->get('title'),
|
||||
$pa->x1, $pa->y1,
|
||||
'freesans', '', $this->titleSize, 'C',
|
||||
$pa->w, $this->titleSize, true, 0
|
||||
);
|
||||
$currentY += $this->titleSize + $this->titleMargin;
|
||||
$usableHeight -= $this->titleSize + $this->titleMargin;
|
||||
}
|
||||
|
||||
// Make the barcode as large as possible while still leaving room for fields
|
||||
$barcodeSize = min($usableHeight * 0.8, $usableWidth * $this->getBarcodeRatio());
|
||||
|
||||
if ($record->has('barcode2d')) {
|
||||
$barcodeX = $pa->x1 + ($usableWidth - $barcodeSize) / 2;
|
||||
|
||||
static::write2DBarcode(
|
||||
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
|
||||
$barcodeX, $currentY,
|
||||
$barcodeSize, $barcodeSize
|
||||
);
|
||||
$currentY += $barcodeSize + $this->barcodeMargin;
|
||||
}
|
||||
|
||||
if ($record->has('fields')) {
|
||||
foreach ($record->get('fields') as $field) {
|
||||
static::writeText(
|
||||
$pdf, $field['label'],
|
||||
$currentX, $currentY,
|
||||
'freesans', '', $this->labelSize, 'L',
|
||||
$usableWidth, $this->labelSize, true, 0
|
||||
);
|
||||
$currentY += $this->labelSize + $this->labelMargin;
|
||||
|
||||
static::writeText(
|
||||
$pdf, $field['value'],
|
||||
$currentX, $currentY,
|
||||
'freemono', 'B', $this->fieldSize, 'L',
|
||||
$usableWidth, $this->fieldSize, true, 0, 0.01
|
||||
);
|
||||
$currentY += $this->fieldSize + $this->fieldMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
|
||||
abstract class Continuous_Landscape_0_59in extends GenericTape
|
||||
{
|
||||
// abstract class for printers using 0.59in width paper in landscape orientation
|
||||
// Using a larger TAPE_WIDTH value to increase font sizes
|
||||
protected const TAPE_WIDTH = 0.59;
|
||||
private float $tapeHeight;
|
||||
protected float $minHeight = 1;
|
||||
|
||||
/**
|
||||
* @param float $length Length of the label in inches (default 2.36in which is 60mm)
|
||||
* @param bool $continuous Whether the tape is continuous or pre-cut
|
||||
* @param float $spacing Spacing between labels for non-continuous tapes (in inches)
|
||||
*/
|
||||
public function __construct($length = 0.6, $continuous = true, $spacing = 0.0) {
|
||||
// Swap width and height for landscape orientation
|
||||
// The height becomes the width, and the length becomes the height
|
||||
parent::__construct($length, self::TAPE_WIDTH, $continuous, $spacing);
|
||||
$this->tapeHeight = $length;
|
||||
|
||||
$this->marginTop = 0.1;
|
||||
$this->marginBottom = 0.1;
|
||||
// Keep small horizontal margins
|
||||
$this->marginLeft = self::TAPE_WIDTH * 0.2;
|
||||
// $this->marginRight = self::TAPE_WIDTH * 0.1;
|
||||
|
||||
// Override font sizes to make them larger
|
||||
// Calculate a larger base font size (3x the default)
|
||||
$baseFontSize = self::TAPE_WIDTH * 0.16; // 3x the default 0.07
|
||||
|
||||
// Recalculate all element sizing based on the larger base font size
|
||||
$this->titleSize = $baseFontSize; // Same as base font size
|
||||
$this->titleMargin = $baseFontSize * 0.3; // 30% of base font size
|
||||
$this->fieldSize = $baseFontSize * 1.1; // 110% of base font size
|
||||
$this->fieldMargin = $baseFontSize * 0.1; // 10% of base font size
|
||||
$this->labelSize = $baseFontSize * 0.7; // 70% of base font size
|
||||
$this->labelMargin = $baseFontSize * -0.1; // -10% of base font size
|
||||
$this->barcodeMargin = $baseFontSize * 0.9; // 20% of base font size
|
||||
$this->tagSize = $baseFontSize * 0.8; // 80% of base font size
|
||||
}
|
||||
|
||||
public function getBarcodeRatio() {
|
||||
return 1.0; // Barcode should use 100% of available height
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the required length for the content
|
||||
*
|
||||
* @param $record The record to calculate length for
|
||||
* @return float The calculated length in inches
|
||||
*/
|
||||
protected function calculateRequiredLength($record) {
|
||||
|
||||
// Calculate length needed for barcode and fields side by side
|
||||
$requiredLength = 0;
|
||||
|
||||
// Add barcode length if present
|
||||
if (($record->has('barcode2d') && $this->getSupport2DBarcode()) ||
|
||||
($record->has('barcode') && $this->getSupport1DBarcode())) {
|
||||
// Use full tape width for barcode size
|
||||
$barcodeSize = self::TAPE_WIDTH;
|
||||
$requiredLength += $barcodeSize + $this->barcodeMargin * 0.3; // Minimal margin
|
||||
}
|
||||
|
||||
// Add fields length if present - calculate based on actual content
|
||||
if ($record->has('fields') && $this->getSupportFields() > 0) {
|
||||
$fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
|
||||
|
||||
// Base width for field area
|
||||
$fieldsWidth = self::TAPE_WIDTH;
|
||||
|
||||
// Calculate additional width based on text length
|
||||
foreach ($fields as $field) {
|
||||
// Get label and value text
|
||||
$labelText = $field['label'] ?? '';
|
||||
$valueText = $field['value'] ?? '';
|
||||
|
||||
// Calculate approximate width needed based on text length
|
||||
// Increase character width to ensure enough space (0.15 inches per character)
|
||||
$labelWidth = strlen($labelText) * 0.09;
|
||||
$valueWidth = strlen($valueText) * 0.09;
|
||||
|
||||
// Use the longer of the two
|
||||
$textWidth = max($labelWidth, $valueWidth);
|
||||
|
||||
// Ensure minimum width and add to total
|
||||
$fieldsWidth = max($fieldsWidth, $textWidth);
|
||||
}
|
||||
|
||||
// Add the calculated width for fields
|
||||
$requiredLength += $fieldsWidth;
|
||||
|
||||
// Add minimal extra space for field padding
|
||||
// Reduce padding to eliminate extraneous space on right edge
|
||||
// $requiredLength += self::TAPE_WIDTH * 0.1;
|
||||
}
|
||||
|
||||
// Ensure minimum length
|
||||
return max($this->minHeight, $requiredLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate text width accurately using the PDF object
|
||||
*
|
||||
* @param $pdf The PDF object
|
||||
* @param string $text The text to measure
|
||||
* @param string $font The font to use
|
||||
* @param string $style The font style
|
||||
* @param float $size The font size
|
||||
* @return float The calculated width
|
||||
*/
|
||||
protected function calculateTextWidth($pdf, $text, $font, $style, $size) {
|
||||
$originalFont = $pdf->getFontFamily();
|
||||
$originalStyle = $pdf->getFontStyle();
|
||||
$originalSize = $pdf->getFontSizePt();
|
||||
|
||||
$pdf->SetFont($font, $style, Helper::convertUnit($size, $this->getUnit(), 'pt', true));
|
||||
$width = $pdf->GetStringWidth($text);
|
||||
|
||||
// Restore original font settings
|
||||
$pdf->SetFont($originalFont, $originalStyle, $originalSize);
|
||||
|
||||
return $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the writeAll method to support dynamic page sizes for continuous tapes
|
||||
*/
|
||||
public function writeAll($pdf, $data) {
|
||||
// Use auto-sizing for continuous tapes, fixed height for die-cut tapes
|
||||
if ($this->continuous) {
|
||||
$data->each(function ($record, $index) use ($pdf) {
|
||||
// Calculate the required length by calling write with calculateOnly=true
|
||||
$requiredLength = $this->write($pdf, $record);
|
||||
|
||||
// If write didn't return a length (old implementation), fall back to calculateRequiredLength
|
||||
if ($requiredLength === null) {
|
||||
$requiredLength = $this->calculateRequiredLength($record);
|
||||
}
|
||||
|
||||
// Temporarily update the height property
|
||||
$originalHeight = $this->height;
|
||||
$this->height = self::TAPE_WIDTH; // Keep height fixed at tape width
|
||||
|
||||
// Add a new page with the calculated dimensions
|
||||
// Keep height fixed at TAPE_WIDTH, use calculated length for width
|
||||
$pdf->AddPage(
|
||||
$this->getOrientation(),
|
||||
[$requiredLength, self::TAPE_WIDTH],
|
||||
false, // Don't reset page number
|
||||
false // Don't reset object ID
|
||||
);
|
||||
|
||||
// Write the content
|
||||
$this->write($pdf, $record);
|
||||
|
||||
// Restore the original height
|
||||
$this->height = $originalHeight;
|
||||
});
|
||||
} else {
|
||||
// Use the default implementation for non-continuous (die-cut) tapes
|
||||
parent::writeAll($pdf, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
class Continuous_Landscape_0_59in_A extends Continuous_Landscape_0_59in
|
||||
{
|
||||
public function getUnit() { return 'in'; }
|
||||
public function getSupportAssetTag() { return false; }
|
||||
public function getSupport1DBarcode() { return true; }
|
||||
public function getSupport2DBarcode() { return true; }
|
||||
public function getSupportFields() { return 2; }
|
||||
public function getSupportLogo() { return false; }
|
||||
public function getSupportTitle() { return false; }
|
||||
|
||||
public function preparePDF($pdf) {
|
||||
$pdf->SetAutoPageBreak(false);
|
||||
}
|
||||
|
||||
public function write($pdf, $record, $calculateOnly = false) {
|
||||
$pa = $this->getPrintableArea();
|
||||
|
||||
$currentX = $pa->x1;
|
||||
$currentY = $pa->y1;
|
||||
$usableWidth = $pa->w;
|
||||
$usableHeight = $pa->h;
|
||||
|
||||
// Calculate required length based on content
|
||||
$requiredLength = 0;
|
||||
|
||||
// Use full usable height for barcode
|
||||
$barcodeSize = $usableHeight;
|
||||
|
||||
// Add barcode width to required length
|
||||
if ($record->has('barcode2d') && $this->getSupport2DBarcode()) {
|
||||
$requiredLength += $barcodeSize;
|
||||
// Add gap between barcode and fields
|
||||
$requiredLength += $this->barcodeMargin;
|
||||
}
|
||||
|
||||
// Calculate fields width using accurate text measurement
|
||||
if ($record->has('fields') && $this->getSupportFields() > 0) {
|
||||
$fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
|
||||
$fieldsWidth = 0;
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$labelText = $field['label'] ?? '';
|
||||
$valueText = $field['value'] ?? '';
|
||||
|
||||
// Calculate accurate width using the PDF object
|
||||
$labelWidth = $this->calculateTextWidth($pdf, $labelText, 'freesans', 'B', $this->labelSize * 1.2);
|
||||
$valueWidth = $this->calculateTextWidth($pdf, $valueText, 'freemono', 'B', $this->fieldSize * 1.3);
|
||||
|
||||
// Use the longer of the two
|
||||
$textWidth = max($labelWidth, $valueWidth);
|
||||
$fieldsWidth = max($fieldsWidth, $textWidth);
|
||||
}
|
||||
|
||||
$requiredLength += $fieldsWidth;
|
||||
}
|
||||
|
||||
// Add more padding to prevent text from being cut off
|
||||
// $requiredLength += self::TAPE_WIDTH * 0.8;
|
||||
|
||||
// Ensure minimum length
|
||||
$requiredLength = max($this->minHeight, $requiredLength);
|
||||
|
||||
// If we're just calculating, return the length
|
||||
if ($calculateOnly) {
|
||||
return $requiredLength;
|
||||
}
|
||||
|
||||
// Otherwise, render the content
|
||||
// Position barcode on the left side
|
||||
if ($record->has('barcode2d') && $this->getSupport2DBarcode()) {
|
||||
// Position at top of usable area
|
||||
static::write2DBarcode(
|
||||
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
|
||||
$currentX, $currentY,
|
||||
$barcodeSize, $barcodeSize
|
||||
);
|
||||
$currentX += $barcodeSize + $this->barcodeMargin;
|
||||
$usableWidth -= $barcodeSize + $this->barcodeMargin;
|
||||
}
|
||||
|
||||
// Position fields to the right of the barcode
|
||||
if ($record->has('fields') && $this->getSupportFields() > 0) {
|
||||
// Limit to the number of supported fields
|
||||
$fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
|
||||
|
||||
// Calculate total height needed for fields
|
||||
$totalFieldsHeight = 0;
|
||||
foreach ($fields as $field) {
|
||||
$totalFieldsHeight += $this->labelSize * 1.2 + $this->labelMargin; // Increased label size by 20%
|
||||
$totalFieldsHeight += $this->fieldSize * 1.3 + $this->fieldMargin * 2; // Increased field size by 30% and margin
|
||||
}
|
||||
|
||||
// Start position - respect top margin
|
||||
$fieldY = $currentY; // $currentY already includes the top margin
|
||||
$fieldWidth = $usableWidth;
|
||||
|
||||
// Calculate available height for fields (respecting margins)
|
||||
$availableHeight = $usableHeight;
|
||||
|
||||
// If fields don't fill available height, adjust spacing proportionally
|
||||
// but don't exceed the available height
|
||||
$scaleFactor = 1.0; // Default scale factor
|
||||
if ($totalFieldsHeight < $availableHeight && count($fields) > 0) {
|
||||
// Scale up to fill available height, but not too much
|
||||
$scaleFactor = min(1.5, $availableHeight / $totalFieldsHeight);
|
||||
} else if ($totalFieldsHeight > $availableHeight && count($fields) > 0) {
|
||||
// Scale down to fit within available height
|
||||
$scaleFactor = $availableHeight / $totalFieldsHeight;
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
// Calculate scaled spacing
|
||||
$labelHeight = $this->labelSize * 1.2 * $scaleFactor;
|
||||
$labelSpacing = $this->labelMargin * $scaleFactor;
|
||||
$fieldHeight = $this->fieldSize * 1.3 * $scaleFactor;
|
||||
$fieldSpacing = $this->fieldMargin * 2 * $scaleFactor;
|
||||
|
||||
// Check if label is empty or null
|
||||
$labelText = $field['label'] ?? '';
|
||||
$valueText = $field['value'] ?? '';
|
||||
|
||||
if (empty(trim($labelText))) {
|
||||
// If label is empty, just render the value at the current Y position
|
||||
static::writeText(
|
||||
$pdf, $valueText,
|
||||
$currentX, $fieldY,
|
||||
'freemono', 'B', $this->fieldSize * 1.3, 'L', // Increased field size by 30%
|
||||
$fieldWidth, $fieldHeight, false, 0, 0.00
|
||||
);
|
||||
$fieldY += ($fieldHeight + $fieldSpacing) + 0.02; // Increased spacing after value
|
||||
} else {
|
||||
// If label has content, render both label and value
|
||||
static::writeText(
|
||||
$pdf, $labelText,
|
||||
$currentX, $fieldY,
|
||||
'freesans', 'B', $this->labelSize * 1.2, 'L', // Increased label size by 20% and made bold
|
||||
$labelWidth, $labelHeight, false, 0,
|
||||
);
|
||||
$fieldY += ($labelHeight + $labelSpacing) + 0.01;
|
||||
|
||||
// Value
|
||||
static::writeText(
|
||||
$pdf, $valueText,
|
||||
$currentX, $fieldY, // Position value directly below label
|
||||
'freemono', 'B', $this->fieldSize * 1.3, 'L', // Increased field size by 30%
|
||||
$fieldWidth, $fieldHeight, false, 0, 0.00
|
||||
);
|
||||
$fieldY += ($fieldHeight + $fieldSpacing) + 0.02; // Increased spacing after value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Labels\Label;
|
||||
|
||||
abstract class GenericTape extends Label
|
||||
{
|
||||
// Default tape width in mm
|
||||
protected const TAPE_WIDTH = 42.0;
|
||||
|
||||
// Tape properties
|
||||
protected float $width;
|
||||
protected float $height;
|
||||
protected bool $continuous;
|
||||
protected float $spacing = 0.0; // Space between labels for non-continuous tapes
|
||||
|
||||
// Margins in mm
|
||||
protected float $marginTop;
|
||||
protected float $marginBottom;
|
||||
protected float $marginLeft;
|
||||
protected float $marginRight;
|
||||
|
||||
// Element sizing in mm
|
||||
protected float $titleSize;
|
||||
protected float $titleMargin;
|
||||
protected float $fieldSize;
|
||||
protected float $fieldMargin;
|
||||
protected float $labelSize;
|
||||
protected float $labelMargin;
|
||||
protected float $barcodeMargin;
|
||||
protected float $tagSize;
|
||||
|
||||
/**
|
||||
* Constructor for generic tape
|
||||
*
|
||||
* @param float $width Width of the tape in mm
|
||||
* @param float $height Height of the label in mm (for continuous tapes, this is the default height)
|
||||
* @param bool $continuous Whether the tape is continuous or pre-cut
|
||||
* @param float $spacing Spacing between labels for non-continuous tapes (in mm)
|
||||
*/
|
||||
public function __construct(float $width, float $height, bool $continuous = true, float $spacing = 0.0) {
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->continuous = $continuous;
|
||||
$this->spacing = $spacing;
|
||||
|
||||
// Calculate base font size (7% of tape width)
|
||||
$baseFontSize = static::TAPE_WIDTH * 0.07;
|
||||
|
||||
// Calculate margin (4% of tape width)
|
||||
$margin = static::TAPE_WIDTH * 0.04;
|
||||
|
||||
// Set margins
|
||||
$this->marginTop = $margin;
|
||||
$this->marginBottom = $margin;
|
||||
$this->marginLeft = $margin;
|
||||
$this->marginRight = $margin;
|
||||
|
||||
// Calculate and set element sizing based on base font size
|
||||
$this->titleSize = $baseFontSize; // Same as base font size
|
||||
$this->titleMargin = $baseFontSize * 0.3; // 30% of base font size
|
||||
$this->fieldSize = $baseFontSize * 1.1; // 110% of base font size
|
||||
$this->fieldMargin = $baseFontSize * 0.1; // 10% of base font size
|
||||
$this->labelSize = $baseFontSize * 0.7; // 70% of base font size
|
||||
$this->labelMargin = $baseFontSize * -0.1; // -10% of base font size
|
||||
$this->barcodeMargin = $baseFontSize * 0.5; // 50% of base font size
|
||||
$this->tagSize = $baseFontSize * 0.8; // 80% of base font size
|
||||
}
|
||||
|
||||
// Unit of measurement
|
||||
public function getUnit() { return 'mm'; }
|
||||
|
||||
// Label dimensions
|
||||
public function getWidth() { return $this->width; }
|
||||
public function getHeight() { return $this->height; }
|
||||
|
||||
// Margins
|
||||
public function getMarginTop() { return $this->marginTop; }
|
||||
public function getMarginBottom() { return $this->marginBottom; }
|
||||
public function getMarginLeft() { return $this->marginLeft; }
|
||||
public function getMarginRight() { return $this->marginRight; }
|
||||
|
||||
|
||||
/**
|
||||
* Check if this is a continuous tape
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isContinuous() {
|
||||
return $this->continuous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get spacing between labels (for die-cut tapes)
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSpacing() {
|
||||
return $this->spacing;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
abstract class Tape_53mm extends GenericTape
|
||||
{
|
||||
// Override tape width to 53mm
|
||||
protected const TAPE_WIDTH = 53.0;
|
||||
|
||||
private float $tapeHeight;
|
||||
|
||||
/**
|
||||
* Constructor for 53mm tape
|
||||
*
|
||||
* @param float $height Height of the label in mm (default 30mm)
|
||||
* @param bool $continuous Whether the tape is continuous or pre-cut
|
||||
* @param float $spacing Spacing between labels for non-continuous tapes (in mm)
|
||||
*/
|
||||
public function __construct(float $height = 30.0, bool $continuous = true, float $spacing = 0.0) {
|
||||
parent::__construct(self::TAPE_WIDTH, $height, $continuous, $spacing);
|
||||
$this->tapeHeight = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the barcode size ratio for calculations
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getBarcodeRatio() {
|
||||
return 0.9; // Barcode should use 90% of available width
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Labels\Tapes\Generic;
|
||||
|
||||
class Tape_53mm_A extends Tape_53mm
|
||||
{
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(40.0, true, 0.0);
|
||||
}
|
||||
|
||||
public function getUnit() { return 'mm'; }
|
||||
public function getSupportAssetTag() { return false; }
|
||||
public function getSupport1DBarcode() { return false; }
|
||||
public function getSupport2DBarcode() { return true; }
|
||||
public function getSupportFields() { return 5; }
|
||||
public function getSupportLogo() { return false; }
|
||||
public function getSupportTitle() { return true; }
|
||||
|
||||
public function preparePDF($pdf) {
|
||||
$pdf->SetAutoPageBreak(false);
|
||||
}
|
||||
|
||||
public function write($pdf, $record) {
|
||||
$pa = $this->getPrintableArea();
|
||||
|
||||
$currentX = $pa->x1;
|
||||
$currentY = $pa->y1;
|
||||
$usableWidth = $pa->w;
|
||||
$usableHeight = $pa->h;
|
||||
|
||||
if ($record->has('title')) {
|
||||
static::writeText(
|
||||
$pdf, $record->get('title'),
|
||||
$pa->x1, $pa->y1,
|
||||
'freesans', '', $this->titleSize, 'C',
|
||||
$pa->w, $this->titleSize, true, 0
|
||||
);
|
||||
$currentY += $this->titleSize + $this->titleMargin;
|
||||
$usableHeight -= $this->titleSize + $this->titleMargin;
|
||||
}
|
||||
|
||||
// Make the barcode as large as possible while still leaving room for fields
|
||||
$barcodeSize = min($usableHeight * 0.8, $usableWidth * $this->getBarcodeRatio());
|
||||
|
||||
if ($record->has('barcode2d')) {
|
||||
$barcodeX = $pa->x1 + ($usableWidth - $barcodeSize) / 2;
|
||||
|
||||
static::write2DBarcode(
|
||||
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
|
||||
$barcodeX, $currentY,
|
||||
$barcodeSize, $barcodeSize
|
||||
);
|
||||
$currentY += $barcodeSize + $this->barcodeMargin;
|
||||
}
|
||||
|
||||
if ($record->has('fields')) {
|
||||
foreach ($record->get('fields') as $field) {
|
||||
static::writeText(
|
||||
$pdf, $field['label'],
|
||||
$currentX, $currentY,
|
||||
'freesans', '', $this->labelSize, 'L',
|
||||
$usableWidth, $this->labelSize, true, 0
|
||||
);
|
||||
$currentY += $this->labelSize + $this->labelMargin;
|
||||
|
||||
static::writeText(
|
||||
$pdf, $field['value'],
|
||||
$currentX, $currentY,
|
||||
'freemono', 'B', $this->fieldSize, 'L',
|
||||
$usableWidth, $this->fieldSize, true, 0, 0.01
|
||||
);
|
||||
$currentY += $this->fieldSize + $this->fieldMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Carbon\Carbon;
|
||||
@@ -22,7 +21,6 @@ class License extends Depreciable
|
||||
|
||||
use SoftDeletes;
|
||||
use CompanyableTrait;
|
||||
use HasUploads;
|
||||
use Loggable, Presentable;
|
||||
protected $injectUniqueIdentifier = true;
|
||||
use ValidatingTrait;
|
||||
@@ -45,7 +43,7 @@ class License extends Depreciable
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string|min:3|max:255',
|
||||
'seats' => 'required|min:1|integer|limit_change:10000', // limit_change is a "pseudo-rule" that translates into 'between', see prepareLimitChangeRule() below
|
||||
'seats' => 'required|min:1|integer',
|
||||
'license_email' => 'email|nullable|max:120',
|
||||
'license_name' => 'string|nullable|max:100',
|
||||
'notes' => 'string|nullable',
|
||||
@@ -115,7 +113,6 @@ class License extends Depreciable
|
||||
'company' => ['name'],
|
||||
'category' => ['name'],
|
||||
'depreciation' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
];
|
||||
protected $appends = ['free_seat_count'];
|
||||
|
||||
@@ -151,14 +148,6 @@ class License extends Depreciable
|
||||
});
|
||||
}
|
||||
|
||||
public function prepareLimitChangeRule($parameters, $field)
|
||||
{
|
||||
$actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
|
||||
$lower_bound = $actual_seat_count - $parameters[0];
|
||||
$upper_bound = $actual_seat_count + $parameters[0];
|
||||
return ["between", ($lower_bound <= 0 ? 1 : $lower_bound), $upper_bound];
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance seat counts
|
||||
*
|
||||
@@ -175,17 +164,21 @@ class License extends Depreciable
|
||||
// On Create, we just make one for each of the seats.
|
||||
$change = abs($oldSeats - $newSeats);
|
||||
if ($oldSeats > $newSeats) {
|
||||
$license->load('licenseseats.user');
|
||||
|
||||
// Need to delete seats... lets see if if we have enough.
|
||||
$seatsAvailableForDelete = $license->licenseseats()->whereNull('assigned_to')->whereNull('asset_id')->limit($change);
|
||||
$seatsAvailableForDelete = $license->licenseseats->reject(function ($seat) {
|
||||
return ((bool) $seat->assigned_to) || ((bool) $seat->asset_id);
|
||||
});
|
||||
|
||||
if ($change > $seatsAvailableForDelete->count()) {
|
||||
Session::flash('error', trans('admin/licenses/message.assoc_users'));
|
||||
|
||||
return false;
|
||||
}
|
||||
$seatsAvailableForDelete->delete();
|
||||
|
||||
for ($i = 1; $i <= $change; $i++) {
|
||||
$seatsAvailableForDelete->pop()->delete();
|
||||
}
|
||||
// Log Deletion of seats.
|
||||
$logAction = new Actionlog;
|
||||
$logAction->item_type = self::class;
|
||||
@@ -414,6 +407,21 @@ class License extends Depreciable
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the license -> action logs -> uploads relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
|
||||
->where('item_type', '=', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\SnipeModel;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Models\User;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -26,7 +25,6 @@ class Location extends SnipeModel
|
||||
protected $presenter = \App\Presenters\LocationPresenter::class;
|
||||
use Presentable;
|
||||
use SoftDeletes;
|
||||
use HasUploads;
|
||||
|
||||
protected $table = 'locations';
|
||||
protected $rules = [
|
||||
@@ -137,17 +135,6 @@ class Location extends SnipeModel
|
||||
return $this->hasMany(\App\Models\User::class, 'location_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the location -> admin user relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find assets with this location as their location_id
|
||||
*
|
||||
@@ -302,6 +289,22 @@ class Location extends SnipeModel
|
||||
return $this->attributes['ldap_ou'] = empty($ldap_ou) ? null : $ldap_ou;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uploads for this location
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany('\App\Models\Actionlog', 'item_id')
|
||||
->where('item_type', '=', Location::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query builder scope to order on parent
|
||||
@@ -371,13 +374,4 @@ class Location extends SnipeModel
|
||||
{
|
||||
return $query->leftJoin('companies as company_sort', 'locations.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on the user that created it
|
||||
*/
|
||||
public function scopeOrderByCreatedByName($query, $order)
|
||||
{
|
||||
return $query->leftJoin('users as admin_sort', 'locations.created_by', '=', 'admin_sort.id')->select('locations.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,10 +89,13 @@ trait Loggable
|
||||
$log->note = $note;
|
||||
$log->action_date = $action_date;
|
||||
|
||||
if (! $log->action_date) {
|
||||
$log->action_date = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$changed = [];
|
||||
$array_to_flip = array_keys($fields_array);
|
||||
$array_to_flip = array_merge($array_to_flip, ['name','status_id','location_id','expected_checkin']);
|
||||
$array_to_flip = array_merge($array_to_flip, ['action_date','name','status_id','location_id','expected_checkin']);
|
||||
$originalValues = array_intersect_key($originalValues, array_flip($array_to_flip));
|
||||
|
||||
|
||||
@@ -179,7 +182,7 @@ trait Loggable
|
||||
$log->note = $note;
|
||||
$log->action_date = $action_date;
|
||||
|
||||
if (!$action_date) {
|
||||
if (! $log->action_date) {
|
||||
$log->action_date = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
@@ -190,7 +193,7 @@ trait Loggable
|
||||
$changed = [];
|
||||
|
||||
$array_to_flip = array_keys($fields_array);
|
||||
$array_to_flip = array_merge($array_to_flip, ['name','status_id','location_id','expected_checkin']);
|
||||
$array_to_flip = array_merge($array_to_flip, ['action_date','name','status_id','location_id','expected_checkin']);
|
||||
|
||||
$originalValues = array_intersect_key($originalValues, array_flip($array_to_flip));
|
||||
|
||||
|
||||
@@ -67,13 +67,11 @@ class Setting extends Model
|
||||
'google_login',
|
||||
'google_client_id',
|
||||
'google_client_secret',
|
||||
'manager_view_enabled',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'label2_asset_logo' => 'boolean',
|
||||
'require_checkinout_notes' => 'boolean',
|
||||
'manager_view_enabled' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,7 @@ class Supplier extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'notes', 'phone', 'fax', 'url', 'email', 'contact', 'address', 'address2', 'city', 'state', 'country', 'zip'];
|
||||
protected $searchableAttributes = ['name'];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
@@ -128,18 +128,6 @@ class Supplier extends SnipeModel
|
||||
return $this->hasMany(\App\Models\Consumable::class, 'supplier_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the supplier -> admin user relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the supplier -> asset maintenances relationship
|
||||
*
|
||||
@@ -209,13 +197,4 @@ class Supplier extends SnipeModel
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on the user that created it
|
||||
*/
|
||||
public function scopeOrderByCreatedByName($query, $order)
|
||||
{
|
||||
return $query->leftJoin('users as admin_sort', 'suppliers.created_by', '=', 'admin_sort.id')->select('suppliers.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
|
||||
trait HasUploads
|
||||
{
|
||||
|
||||
public function uploads() {
|
||||
return $this->hasMany(Actionlog::class, 'item_id')
|
||||
->where('item_type', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -164,7 +164,7 @@ trait Searchable
|
||||
}
|
||||
}
|
||||
// I put this here because I only want to add the concat one time in the end of the user relation search
|
||||
if(($relation == 'adminuser') || ($relation == 'user')) {
|
||||
if($relation == 'user') {
|
||||
$query->orWhereRaw(
|
||||
$this->buildMultipleColumnSearch([
|
||||
'users.first_name',
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
@@ -19,7 +18,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
@@ -28,10 +26,9 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
{
|
||||
use HasFactory;
|
||||
use CompanyableTrait;
|
||||
use HasUploads;
|
||||
|
||||
protected $presenter = \App\Presenters\UserPresenter::class;
|
||||
use SoftDeletes, ValidatingTrait, Loggable;
|
||||
use SoftDeletes, ValidatingTrait;
|
||||
use Authenticatable, Authorizable, CanResetPassword, HasApiTokens;
|
||||
use UniqueUndeletedTrait;
|
||||
use Notifiable;
|
||||
@@ -171,15 +168,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
}
|
||||
|
||||
|
||||
public function isAvatarExternal() {
|
||||
// Check if it's a google avatar or some external avatar
|
||||
if (Str::startsWith($this->avatar, ['http://', 'https://'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally check the user permission for the given section
|
||||
*
|
||||
@@ -532,7 +520,21 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
return $this->hasMany(\App\Models\Asset::class, 'id')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes the user -> uploads relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function uploads()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
|
||||
->where('item_type', self::class)
|
||||
->where('action_type', '=', 'uploaded')
|
||||
->whereNotNull('filename')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the user -> acceptances relationship
|
||||
@@ -549,25 +551,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the user -> eula relationship
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
* @since [v8.1.16]
|
||||
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
|
||||
*/
|
||||
public function eulas()
|
||||
{
|
||||
return $this->hasMany(Actionlog::class, 'target_id')
|
||||
->with('item')
|
||||
->select(['id', 'target_id', 'target_type', 'action_type', 'filename', 'accept_signature', 'created_at', 'note', 'item_id', 'item_type'])
|
||||
->where('target_type', self::class)
|
||||
->where('action_type', 'accepted')
|
||||
->whereNotNull('filename')
|
||||
->whereNotNull('accept_signature')
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the user -> requested assets relationship
|
||||
*
|
||||
@@ -973,75 +956,5 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all direct and indirect subordinates for this user.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getAllSubordinates()
|
||||
{
|
||||
$subordinates = collect();
|
||||
$this->fetchSubordinatesRecursive($this, $subordinates);
|
||||
return $subordinates->unique('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all direct and indirect subordinates for this user, including self.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getAllSubordinatesIncludingSelf()
|
||||
{
|
||||
$subordinates = collect([$this]);
|
||||
$this->fetchSubordinatesRecursive($this, $subordinates);
|
||||
return $subordinates->unique('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper function to fetch subordinates.
|
||||
*
|
||||
* @param User $manager
|
||||
* @param \Illuminate\Support\Collection $subs
|
||||
*/
|
||||
protected function fetchSubordinatesRecursive(User $manager, \Illuminate\Support\Collection &$subs)
|
||||
{
|
||||
// Eager load 'managesUsers' to prevent N+1 queries in recursion
|
||||
$directSubordinates = $manager->managesUsers()->with('managesUsers')->get();
|
||||
|
||||
foreach ($directSubordinates as $directSubordinate) {
|
||||
// Add subordinate if not already in the collection
|
||||
if (!$subs->contains('id', $directSubordinate->id)) {
|
||||
$subs->push($directSubordinate);
|
||||
// Recursive call for this subordinate's subordinates
|
||||
$this->fetchSubordinatesRecursive($directSubordinate, $subs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user is a direct or indirect manager of the given user.
|
||||
*
|
||||
* @param User $userToCheck
|
||||
* @return bool
|
||||
*/
|
||||
public function isManagerOf(User $userToCheck): bool
|
||||
{
|
||||
// Optimization: If it's the same user, they are not their own manager
|
||||
if ($this->id === $userToCheck->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Eager load manager relationship to potentially reduce queries in the loop
|
||||
$manager = $userToCheck->load('manager')->manager;
|
||||
while ($manager) {
|
||||
if ($manager->id === $this->id) {
|
||||
return true;
|
||||
}
|
||||
// Move up the hierarchy (load relationship if not already loaded)
|
||||
$manager = $manager->load('manager')->manager;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ class AssetObserver
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->action_date = date('Y-m-d H:i:s');
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->log_meta = json_encode($changed);
|
||||
@@ -109,7 +108,6 @@ class AssetObserver
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class; // can we instead say $logAction->item = $asset ?
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->action_date = date('Y-m-d H:i:s');
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->created_by = auth()->id();
|
||||
if($asset->imported) {
|
||||
@@ -130,7 +128,6 @@ class AssetObserver
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->action_date = date('Y-m-d H:i:s');
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
@@ -146,7 +143,6 @@ class AssetObserver
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->action_date = date('Y-m-d H:i:s');
|
||||
$logAction->created_at = date('Y-m-d H:i:s');
|
||||
$logAction->created_by = auth()->id();
|
||||
$logAction->logaction('restore');
|
||||
|
||||
@@ -21,7 +21,6 @@ class AccessoryPresenter extends Presenter
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
'printIgnore' => true,
|
||||
], [
|
||||
'field' => 'image',
|
||||
'searchable' => false,
|
||||
@@ -173,7 +172,6 @@ class AccessoryPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'accessoriesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -242,7 +240,6 @@ class AccessoryPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'accessoriesInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ class AssetAuditPresenter extends Presenter
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
'printIgnore' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
@@ -267,7 +266,6 @@ class AssetAuditPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'hardwareAuditFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
|
||||
@@ -150,7 +150,6 @@ class AssetMaintenancesPresenter extends Presenter
|
||||
'title' => trans('table.actions'),
|
||||
'visible' => true,
|
||||
'formatter' => 'maintenancesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ class AssetModelPresenter extends Presenter
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
'printIgnore' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'id',
|
||||
@@ -186,7 +185,6 @@ class AssetModelPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'modelsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
|
||||
@@ -22,7 +22,6 @@ class AssetPresenter extends Presenter
|
||||
'field' => 'checkbox',
|
||||
'checkbox' => true,
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
'printIgnore' => true,
|
||||
], [
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
@@ -109,12 +108,6 @@ class AssetPresenter extends Presenter
|
||||
'title' => trans('general.employee_number'),
|
||||
'visible' => false,
|
||||
'formatter' => 'employeeNumFormatter',
|
||||
],[
|
||||
'field' => 'jobtitle',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/users/table.title'),
|
||||
'visible' => false,
|
||||
], [
|
||||
'field' => 'location',
|
||||
'searchable' => true,
|
||||
@@ -347,7 +340,6 @@ class AssetPresenter extends Presenter
|
||||
'title' => trans('general.checkin').'/'.trans('general.checkout'),
|
||||
'visible' => true,
|
||||
'formatter' => 'hardwareInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
$layout[] = [
|
||||
@@ -357,7 +349,6 @@ class AssetPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'hardwareActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
@@ -425,7 +416,6 @@ class AssetPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'accessoriesInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -112,8 +112,7 @@ class CategoryPresenter extends Presenter
|
||||
'sortable' => false,
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'categoriesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
'formatter' => 'categoriesActionsFormatter',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -142,7 +142,6 @@ class CompanyPresenter extends Presenter
|
||||
'title' => trans('table.actions'),
|
||||
'visible' => true,
|
||||
'formatter' => 'companiesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -169,7 +169,6 @@ class ComponentPresenter extends Presenter
|
||||
'title' => trans('general.checkin').'/'.trans('general.checkout'),
|
||||
'visible' => true,
|
||||
'formatter' => 'componentsInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
$layout[] = [
|
||||
@@ -179,7 +178,6 @@ class ComponentPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'componentsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
|
||||
@@ -72,6 +72,14 @@ class ConsumablePresenter extends Presenter
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/consumables/general.item_no'),
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
'formatter' => 'minAmtFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
@@ -88,14 +96,6 @@ class ConsumablePresenter extends Presenter
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
'formatter' => 'minAmtFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'location',
|
||||
'searchable' => true,
|
||||
@@ -173,7 +173,6 @@ class ConsumablePresenter extends Presenter
|
||||
'title' => trans('table.actions'),
|
||||
'visible' => true,
|
||||
'formatter' => 'consumablesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ class DepreciationPresenter extends Presenter
|
||||
'title' => trans('table.actions'),
|
||||
'visible' => true,
|
||||
'formatter' => 'depreciationsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Presenters;
|
||||
|
||||
/**
|
||||
* Class AccessoryPresenter
|
||||
*/
|
||||
class HistoryPresenter extends Presenter
|
||||
{
|
||||
/**
|
||||
* Json Column Layout for bootstrap table
|
||||
* @return string
|
||||
*/
|
||||
public static function dataTableLayout($serial = false)
|
||||
{
|
||||
$extra = [];
|
||||
$layout_start = [
|
||||
[
|
||||
'id' => 'id',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.id'),
|
||||
'visible' => false,
|
||||
'class' => 'hidden-xs',
|
||||
],
|
||||
[
|
||||
'field' => 'icon',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/hardware/table.icon'),
|
||||
'visible' => true,
|
||||
'class' => 'hidden-xs',
|
||||
'formatter' => 'iconFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'created_at',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.created_at'),
|
||||
'visible' => true,
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'created_by',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => true,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'action_date',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.action_date'),
|
||||
'visible' => false,
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'action_type',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.action'),
|
||||
'visible' => true,
|
||||
],
|
||||
[
|
||||
'field' => 'item',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.item'),
|
||||
'visible' => true,
|
||||
'formatter' => 'polymorphicItemFormatter',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
if ($serial) {
|
||||
$extra = [
|
||||
[
|
||||
'field' => 'item.serial',
|
||||
'title' => trans('admin/hardware/table.serial'),
|
||||
'visible' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$layout_end = [
|
||||
[
|
||||
'field' => 'target',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.target'),
|
||||
'visible' => true,
|
||||
'formatter' => 'polymorphicItemFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'file',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.file_name'),
|
||||
'visible' => true,
|
||||
'formatter' => 'fileUploadNameFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'file_download',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.download'),
|
||||
'visible' => true,
|
||||
'formatter' => 'fileUploadFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'note',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
'title' => trans('general.notes'),
|
||||
'formatter' => 'notesFormatter'
|
||||
],
|
||||
[
|
||||
'field' => 'signature_file',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.signature'),
|
||||
'visible' => false,
|
||||
'formatter' => 'imageFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'log_meta',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'visible' => true,
|
||||
'title' => trans('admin/hardware/table.changed'),
|
||||
'formatter' => 'changeLogFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'remote_ip',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('admin/settings/general.login_ip'),
|
||||
],
|
||||
[
|
||||
'field' => 'user_agent',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('admin/settings/general.login_user_agent'),
|
||||
],
|
||||
[
|
||||
'field' => 'action_source',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'visible' => false,
|
||||
'title' => trans('general.action_source'),
|
||||
],
|
||||
];
|
||||
|
||||
$merged = array_merge($layout_start, $extra, $layout_end);
|
||||
return json_encode($merged);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -202,7 +202,6 @@ class LicensePresenter extends Presenter
|
||||
'title' => trans('general.checkin').'/'.trans('general.checkout'),
|
||||
'visible' => true,
|
||||
'formatter' => 'licensesInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
$layout[] = [
|
||||
@@ -212,7 +211,6 @@ class LicensePresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'licensesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
|
||||
@@ -18,8 +18,6 @@ class LocationPresenter extends Presenter
|
||||
'field' => 'bulk_selectable',
|
||||
'checkbox' => true,
|
||||
'formatter' => 'checkboxEnabledFormatter',
|
||||
'titleTooltip' => trans('general.select_all_none'),
|
||||
'printIgnore' => true,
|
||||
], [
|
||||
'field' => 'id',
|
||||
'searchable' => false,
|
||||
@@ -210,16 +208,7 @@ class LocationPresenter extends Presenter
|
||||
'title' => trans('general.created_at'),
|
||||
'visible' => false,
|
||||
'formatter' => 'dateDisplayFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'created_by',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => false,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],[
|
||||
], [
|
||||
'field' => 'actions',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
|
||||
@@ -163,7 +163,6 @@ class ManufacturerPresenter extends Presenter
|
||||
'title' => trans('table.actions'),
|
||||
'visible' => true,
|
||||
'formatter' => 'manufacturersActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'title' => trans('general.checkin').'/'.trans('general.checkout'),
|
||||
'visible' => true,
|
||||
'formatter' => 'kitsInOutFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
$layout[] = [
|
||||
@@ -71,7 +70,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'kitsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
];
|
||||
|
||||
return json_encode($layout);
|
||||
@@ -123,7 +121,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'kits_modelsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -176,7 +173,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'kits_licensesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -229,7 +225,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'kits_accessoriesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -282,7 +277,6 @@ class PredefinedKitPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'kits_consumablesActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@ class StatusLabelPresenter extends Presenter
|
||||
'switchable' => false,
|
||||
'title' => trans('table.actions'),
|
||||
'formatter' => 'statuslabelsActionsFormatter',
|
||||
'printIgnore' => true,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user