Compare commits

..

15 Commits

Author SHA1 Message Date
snipe
07e252d58f This runs, but is broken
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 13:36:18 +01:00
snipe
5143b87b3a Revert "Ignore build assets"
This reverts commit a1c85fef14.

Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:08:37 +01:00
snipe
b1ef176dbc ¯\_(ツ)_/¯
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:08:05 +01:00
snipe
4226642b1b ¯\_(ツ)_/¯
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:07:57 +01:00
snipe
e40ba0f79c Set the URLs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:07:48 +01:00
snipe
b8c0279105 Revert "Ignore build assets"
This reverts commit a1c85fef14.
2025-07-28 10:51:16 +01:00
snipe
aae215a968 Updated package.lock
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 10:18:50 +01:00
snipe
bb847b9d99 Renamed vite.config.js to vite.config.jsx
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 10:18:42 +01:00
Shift
a1c85fef14 Ignore build assets 2025-07-28 08:55:52 +00:00
Shift
c65335a61e Add .vue extension to Vue imports 2025-07-28 08:55:52 +00:00
Shift
f95b014d2b Use ES modules 2025-07-28 08:55:51 +00:00
Shift
91e4fddd83 Remove webpack.mix.js 2025-07-28 08:55:51 +00:00
Shift
2af991023d Configure Vite 2025-07-28 08:55:50 +00:00
Shift
a993ab97c6 Update build scripts 2025-07-28 08:55:50 +00:00
Shift
f002c65608 Add npm dependencies 2025-07-28 08:55:50 +00:00
1086 changed files with 297064 additions and 34549 deletions

View File

@@ -4189,24 +4189,6 @@
"contributions": [
"code"
]
},
{
"login": "mckaygerhard",
"name": "Герхард PICCORO Lenz McKAY ",
"avatar_url": "https://avatars.githubusercontent.com/u/1571724?v=4",
"profile": "https://github-readme-stats.vercel.app/api?username=mckaygerhard",
"contributions": [
"code"
]
},
{
"login": "FlorestanII",
"name": "Johannes Pollitt",
"avatar_url": "https://avatars.githubusercontent.com/u/15015119?v=4",
"profile": "https://github.com/FlorestanII",
"contributions": [
"code"
]
}
]
}

View File

@@ -68,7 +68,6 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<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/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -1,74 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutRequest;
use Illuminate\Console\Command;
class CleanOldCheckoutRequests extends Command
{
private int $deletions = 0;
private int $skips = 0;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-old-checkout-requests';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes checkout requests that reference deleted assets or users.';
/**
* Execute the console command.
*/
public function handle()
{
$requests = CheckoutRequest::with([
'user' => function ($query) {
$query->withTrashed();
},
'requestedItem' => function ($query) {
$query->withTrashed();
},
])->get();
$this->info("Processing {$requests->count()} checkout requests");
$this->withProgressBar($requests, function ($request) {
if ($this->shouldForceDelete($request)) {
$request->forceDelete();
$this->deletions++;
return;
}
if ($this->shouldSoftDelete($request)) {
$request->delete();
$this->deletions++;
return;
}
$this->skips++;
});
$this->info("Final deletion count: $this->deletions, and skip count: $this->skips");
return 0;
}
private function shouldForceDelete(CheckoutRequest $request)
{
// check if the requestable or user relationship is null
return !$request->requestable || !$request->user;
}
private function shouldSoftDelete(CheckoutRequest $request)
{
return $request->requestable->trashed() || $request->user->trashed();
}
}

View File

@@ -96,7 +96,7 @@ class MoveUploadsToNewDisk extends Command
$private_uploads['assets'] = glob('storage/private_uploads/assets'."/*.*");
$private_uploads['signatures'] = glob('storage/private_uploads/signatures'."/*.*");
$private_uploads['audits'] = glob('storage/private_uploads/audits'."/*.*");
$private_uploads['assetmodels'] = glob('storage/private_uploads/models'."/*.*");
$private_uploads['assetmodels'] = glob('storage/private_uploads/assetmodels'."/*.*");
$private_uploads['imports'] = glob('storage/private_uploads/imports'."/*.*");
$private_uploads['licenses'] = glob('storage/private_uploads/licenses'."/*.*");
$private_uploads['users'] = glob('storage/private_uploads/users'."/*.*");

View File

@@ -62,19 +62,19 @@ class Purge extends Command
$assetcount = $assets->count();
$this->info($assets->count().' assets purged.');
$asset_assoc = 0;
$maintenances = 0;
$asset_maintenances = 0;
foreach ($assets as $asset) {
$this->info('- Asset "'.$asset->present()->name().'" deleted.');
$asset_assoc += $asset->assetlog()->count();
$asset->assetlog()->forceDelete();
$maintenances += $asset->maintenances()->count();
$asset->maintenances()->forceDelete();
$asset_maintenances += $asset->assetmaintenances()->count();
$asset->assetmaintenances()->forceDelete();
$asset->forceDelete();
}
$this->info($asset_assoc.' corresponding log records purged.');
$this->info($maintenances.' corresponding maintenance records purged.');
$this->info($asset_maintenances.' corresponding maintenance records purged.');
$locations = Location::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($locations->count().' locations purged.');

View File

@@ -243,8 +243,6 @@ class RestoreFromBackup extends Command
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels',
'storage/private_uploads/maintenances',
'storage/private_uploads/models',
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
@@ -262,10 +260,9 @@ class RestoreFromBackup extends Command
];
$public_dirs = [
'public/uploads/accessories',
'public/uploads/assetmodels',
'public/uploads/maintenances',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/categories',
'public/uploads/companies',
'public/uploads/components',

View File

@@ -138,13 +138,13 @@ class Handler extends ExceptionHandler
if (in_array('bulkedit', $ids, true)) {
$error_array = session()->get('bulk_asset_errors');
return redirect()
->route('hardware.index')
->route('hardware.bulkedit')
->withErrors($error_array, 'bulk_asset_errors')
->withInput();
}
// This gets the MVC model name from the exception and formats in a way that's less fugly
$model_name = trim(strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel()))))));
// This gets the MVC model name from the exception and formats in a way that's less fugly
$model_name = strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel())))));
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
// Sigh.
@@ -160,7 +160,9 @@ class Handler extends ExceptionHandler
$route = 'maintenances.index';
} elseif ($route === 'licenseseats.index') {
$route = 'licenses.index';
} elseif (($route === 'customfieldsets.index') || ($route === 'customfields.index')) {
} elseif ($route === 'customfields.index') {
$route = 'fields.index';
} elseif ($route === 'customfieldsets.index') {
$route = 'fields.index';
}

View File

@@ -1197,30 +1197,19 @@ class Helper
'webp' => 'far fa-image',
'avif' => 'far fa-image',
'svg' => 'fas fa-vector-square',
// word
'doc' => 'far fa-file-word',
'docx' => 'far fa-file-word',
// Excel
'xls' => 'far fa-file-excel',
'xlsx' => 'far fa-file-excel',
'ods' => 'far fa-file-excel',
// Presentation
'ppt' => 'far fa-file-powerpoint',
'odp' => 'far fa-file-powerpoint',
// archive
'zip' => 'fas fa-file-archive',
'rar' => 'fas fa-file-archive',
//Text
'odt' => 'far fa-file-alt',
'txt' => 'far fa-file-alt',
'rtf' => 'far fa-file-alt',
'xml' => 'fas fa-code',
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
@@ -1554,6 +1543,11 @@ class Helper
// return to previous page
if ($redirect_option === 'back') {
if ($backUrl === route('home')) {
return redirect()->to($backUrl)
->with('warning', trans('general.page_error'));
}
return redirect()->to($backUrl);
}

View File

@@ -43,8 +43,6 @@ class IconHelper
return 'fa-regular fa-envelope';
case 'phone':
return 'fa-solid fa-phone';
case 'mobile':
return 'fas fa-mobile-screen-button';
case 'long-arrow-right':
return 'fas fa-long-arrow-alt-right';
case 'download':
@@ -153,7 +151,6 @@ class IconHelper
case 'location':
return 'fas fa-map-marker-alt';
case 'superadmin':
case 'admin':
return 'fas fa-crown';
case 'print':
return 'fa-solid fa-print';

View File

@@ -27,45 +27,6 @@ class StorageHelper
}
}
public static function getMediaType($file_with_path) {
// Get the file extension and determine the media type
if (Storage::exists($file_with_path)) {
$fileinfo = pathinfo($file_with_path);
$extension = strtolower($fileinfo['extension']);
switch ($extension) {
case 'avif':
case 'jpg':
case 'png':
case 'gif':
case 'svg':
case 'webp':
return 'image';
case 'pdf':
return 'pdf';
case 'mp3':
case 'wav':
case 'ogg':
return 'audio';
case 'mp4':
case 'webm':
case 'mov':
return 'video';
case 'doc':
case 'docx':
return 'document';
case 'txt':
return 'text';
case 'xls':
case 'xlsx':
case 'ods':
return 'spreadsheet';
default:
return $extension; // Default for unknown types
}
}
return null;
}
/**
* This determines the file types that should be allowed inline and checks their fileinfo extension
@@ -91,6 +52,7 @@ class StorageHelper
'pdf',
'png',
'svg',
'svg',
'wav',
'webm',
'webp',

View File

@@ -77,25 +77,9 @@ class AccessoriesController extends Controller
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
if ($request->has('use_cloned_image')) {
$cloned_model_img = Accessory::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'accessories/'.$new_image_name;
Storage::disk('public')->copy('accessories/'.$cloned_model_img->image, $new_image);
$accessory->image = $new_image_name;
}
} else {
$accessory = $request->handleImages($accessory);
}
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
$accessory = $request->handleImages($accessory);
session()->put(['redirect_option' => $request->get('redirect_option')]);
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
@@ -130,12 +114,11 @@ class AccessoriesController extends Controller
$this->authorize('create', Accessory::class);
$cloned = clone $accessory;
$accessory_to_clone = $accessory;
$cloned->id = null;
$cloned->deleted_at = '';
$cloned->location_id = null;
return view('accessories/edit')
->with('cloned_model', $accessory_to_clone)
->with('item', $cloned);
}

View File

@@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Accessories;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Accessory;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class AccessoriesFilesController extends Controller
{
/**
* Validates and stores files associated with a accessory.
*
* @param UploadFileRequest $request
* @param int $accessoryId
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @todo Switch to using the AssetFileRequest form request validator.
*/
public function store(UploadFileRequest $request, $accessoryId = null) : RedirectResponse
{
if (config('app.lock_passwords')) {
return redirect()->route('accessories.show', ['accessory'=>$accessoryId])->with('error', trans('general.feature_disabled'));
}
$accessory = Accessory::find($accessoryId);
if (isset($accessory->id)) {
$this->authorize('accessories.files', $accessory);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/accessories')) {
Storage::makeDirectory('private_uploads/accessories', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/accessories/', 'accessory-'.$accessory->id, $file);
//Log the upload to the log
$accessory->logUpload($file_name, e($request->input('notes')));
}
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('error', trans('general.no_files_uploaded'));
}
// Prepare the error message
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**
* Deletes the selected accessory file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $accessoryId
* @param int $fileId
*/
public function destroy($accessoryId = null, $fileId = null) : RedirectResponse
{
if ($accessory = Accessory::find($accessoryId)) {
$this->authorize('update', $accessory);
if ($log = Actionlog::find($fileId)) {
if (Storage::exists('private_uploads/accessories/'.$log->filename)) {
try {
Storage::delete('private_uploads/accessories/' . $log->filename);
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
} catch (\Exception $e) {
Log::debug($e);
return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist'));
}
}
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
/**
* Allows the selected file to be viewed.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.4]
* @param int $accessoryId
* @param int $fileId
*/
public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
{
// the accessory is valid
if ($accessory = Accessory::find($accessoryId)) {
$this->authorize('view', $accessory);
$this->authorize('accessories.files', $accessory);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
$file = 'private_uploads/accessories/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
}
}
return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
}

View File

@@ -232,7 +232,6 @@ class AcceptanceController extends Controller
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => $path_logo,
'date_settings' => $branding_settings->date_display_format,
'admin' => auth()->user()->present()?->fullName,
];
if ($pdf_view_route!='') {
@@ -348,7 +347,6 @@ class AcceptanceController extends Controller
$acceptance->decline($sig_filename, $request->input('note'));
$acceptance->notify(new AcceptanceAssetDeclinedNotification($data));
Log::debug('New event acceptance.');
event(new CheckoutDeclined($acceptance));
$return_msg = trans('admin/users/message.declined');
}
@@ -358,16 +356,13 @@ class AcceptanceController extends Controller
$recipient = User::find($acceptance->alert_on_response_id);
if ($recipient) {
Log::debug('Attempting to send email acceptance.');
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
$acceptance,
$recipient,
$request->input('asset_acceptance') === 'accepted',
));
Log::debug('Send email notification sucess on checkout acceptance response.');
}
} catch (Exception $e) {
Log::error($e->getMessage());
Log::warning($e);
}
}

View File

@@ -4,11 +4,11 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\MaintenancesTransformer;
use App\Http\Transformers\AssetMaintenancesTransformer;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use App\Models\Company;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
@@ -18,13 +18,13 @@ use Illuminate\Http\JsonResponse;
*
* @version v2.0
*/
class MaintenancesController extends Controller
class AssetMaintenancesController extends Controller
{
/**
* Generates the JSON response for asset maintenances listing view.
*
* @see MaintenancesController::getIndex() method that generates view
* @see AssetMaintenancesController::getIndex() method that generates view
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
@@ -33,7 +33,7 @@ class MaintenancesController extends Controller
{
$this->authorize('view', Asset::class);
$maintenances = Maintenance::select('maintenances.*')
$maintenances = AssetMaintenance::select('asset_maintenances.*')
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'adminuser');
if ($request->filled('search')) {
@@ -45,11 +45,11 @@ class MaintenancesController extends Controller
}
if ($request->filled('supplier_id')) {
$maintenances->where('maintenances.supplier_id', '=', $request->input('supplier_id'));
$maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('created_by')) {
$maintenances->where('maintenances.created_by', '=', $request->input('created_by'));
$maintenances->where('asset_maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('asset_maintenance_type')) {
@@ -63,7 +63,7 @@ class MaintenancesController extends Controller
$allowed_columns = [
'id',
'name',
'title',
'asset_maintenance_time',
'asset_maintenance_type',
'cost',
@@ -112,7 +112,7 @@ class MaintenancesController extends Controller
$total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
return (new MaintenancesTransformer())->transformMaintenances($maintenances, $total);
return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
}
@@ -121,23 +121,22 @@ class MaintenancesController extends Controller
/**
* Validates and stores the new asset maintenance
*
* @see MaintenancesController::getCreate() method for the form
* @see AssetMaintenancesController::getCreate() method for the form
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
public function store(ImageUploadRequest $request) : JsonResponse | array
public function store(Request $request) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// create a new model instance
$maintenance = new Maintenance();
$maintenance = new AssetMaintenance();
$maintenance->fill($request->all());
$maintenance->created_by = auth()->id();
$maintenance = $request->handleImages($maintenance);
// Was the asset maintenance created?
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
}
@@ -158,11 +157,11 @@ class MaintenancesController extends Controller
{
$this->authorize('update', Asset::class);
if ($maintenance = Maintenance::with('asset')->find($id)) {
if ($maintenance = AssetMaintenance::with('asset')->find($id)) {
// Can this user manage this asset?
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
}
// The asset this miantenance is attached to is not valid or has been deleted
@@ -173,13 +172,13 @@ class MaintenancesController extends Controller
$maintenance->fill($request->all());
if ($maintenance->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.edit.success')));
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id])));
}
@@ -187,20 +186,20 @@ class MaintenancesController extends Controller
* Delete an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
*/
public function destroy($maintenanceId) : JsonResponse | array
public function destroy($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
$maintenance = Maintenance::findOrFail($maintenanceId);
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
$maintenance->delete();
$assetMaintenance->delete();
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.delete.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
}
@@ -209,19 +208,19 @@ class MaintenancesController extends Controller
* View an asset maintenance
*
* @author A. Gianotto <snipe@snipe.net>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v4.0]
*/
public function show($maintenanceId) : JsonResponse | array
public function show($assetMaintenanceId) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$maintenance = Maintenance::findOrFail($maintenanceId);
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot view a maintenance for that asset'));
}
return (new MaintenancesTransformer())->transformMaintenance($maintenance);
return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
}
}

View File

@@ -117,20 +117,15 @@ class AssetsController extends Controller
'jobtitle',
];
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
}
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
}
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
}
$assets = Asset::select('assets.*')
@@ -146,7 +141,6 @@ class AssetsController extends Controller
'model.category',
'model.manufacturer',
'model.fieldset',
'model.depreciation',
'supplier'
); // it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.

View File

@@ -228,16 +228,11 @@ class ConsumablesController extends Controller
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'avatar' => ($consumable_assignment->user) ? e($consumable_assignment->user->present()->gravatar) : '',
'user' => ($consumable_assignment->user) ? [
'id' => (int) $consumable_assignment->user->id,
'name'=> e($consumable_assignment->user->present()->fullName()),
] : null,
'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'note' => ($consumable_assignment->note) ? e($consumable_assignment->note) : null,
'created_by' => ($consumable_assignment->adminuser) ? [
'id' => (int) $consumable_assignment->adminuser->id,
'name'=> e($consumable_assignment->adminuser->present()->fullName()),
] : null,
'admin' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null, // legacy, so we don't change the shape of the response
'created_by' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null,
];
}

View File

@@ -150,11 +150,8 @@ class SettingsController extends Controller
if (!config('app.lock_passwords')) {
try {
Notification::send(Setting::first(), new MailTest());
Log::debug('Attempting to sending to '.config('mail.reply_to.address'));
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (\Exception $e) {
Log::error('Mail sent error using '.config('mail.reply_to.address') .': '. $e->getMessage());
Log::debug($e);
return response()->json(['message' => $e->getMessage()], 500);
}
}
@@ -318,4 +315,4 @@ class SettingsController extends Controller
}
}
}

View File

@@ -194,7 +194,7 @@ class SuppliersController extends Controller
public function destroy($id) : JsonResponse
{
$this->authorize('delete', Supplier::class);
$supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
$supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances as asset_maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
$this->authorize('delete', $supplier);
@@ -202,8 +202,8 @@ class SuppliersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
}
if ($supplier->maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count])));
if ($supplier->asset_maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count])));
}
if ($supplier->licenses_count > 0) {

View File

@@ -7,9 +7,18 @@ 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;
@@ -19,6 +28,45 @@ class UploadedFilesController extends Controller
{
static $map_object_type = [
'accessories' => Accessory::class,
'assets' => Asset::class,
'components' => Component::class,
'consumables' => Consumable::class,
'hardware' => Asset::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/',
'hardware' => 'private_uploads/assets/',
'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',
'hardware' => 'asset',
'licenses' => 'license',
'locations' => 'location',
'models' => 'model',
'users' => 'user',
];
/**
* List files for an object
*
@@ -50,22 +98,15 @@ class UploadedFilesController extends Controller
'created_at',
];
$uploads = Actionlog::select('action_logs.*')
->whereNotNull('filename')
->where('item_type', self::$map_object_type[$object_type])
->where('item_id', $object->id)
->where('action_type', '=', 'uploaded')
->with('adminuser');
$offset = ($request->input('offset') > $uploads->count()) ? $uploads->count() : abs($request->input('offset'));
$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') : 'created_at';
$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 searching across all relations
// and we generally won't need that here
// 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(
@@ -76,10 +117,8 @@ class UploadedFilesController extends Controller
);
}
$total = $uploads->count();
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
return (new UploadedFilesTransformer())->transformFiles($uploads, $total);
return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count());
}

View File

@@ -20,7 +20,6 @@ use App\Models\Consumable;
use App\Models\License;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
@@ -70,7 +69,6 @@ class UsersController extends Controller
'users.notes',
'users.permissions',
'users.phone',
'users.mobile',
'users.state',
'users.two_factor_enrolled',
'users.two_factor_optin',
@@ -110,26 +108,10 @@ class UsersController extends Controller
$users = $users->where('users.activated', '=', $request->input('activated'));
}
if ($request->input('admins') == 'true') {
$users = $users->OnlyAdminsAndSuperAdmins();
}
if ($request->input('superadmins') == 'true') {
$users = $users->OnlySuperAdmins();
}
if ($request->filled('company_id')) {
$users = $users->where('users.company_id', '=', $request->input('company_id'));
}
if ($request->filled('phone')) {
$users = $users->where('users.phone', '=', $request->input('phone'));
}
if ($request->filled('mobile')) {
$users = $users->where('users.mobile', '=', $request->input('mobile'));
}
if ($request->filled('location_id')) {
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
@@ -302,7 +284,6 @@ class UsersController extends Controller
'manages_users_count',
'manages_locations_count',
'phone',
'mobile',
'address',
'city',
'state',
@@ -833,37 +814,4 @@ class UsersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);
}
/**
* Run the LDAP sync command to import users from LDAP via API.
*
* @author A. Gianotto <snipe@snipe.net>
* @since 8.2.2
*
* @return \Illuminate\Http\JsonResponse
*/
public function syncLdapUsers(Request $request)
{
$this->authorize('update', User::class);
// Call Artisan LDAP import command.
Artisan::call('snipeit:ldap-sync', ['--location_id' => $request->input('location_id'), '--json_summary' => true]);
// Collect and parse JSON summary.
$ldap_results_json = Artisan::output();
$ldap_results = json_decode($ldap_results_json, true);
if (!$ldap_results) {
return response()->json(Helper::formatStandardApiResponse('error', null,trans('general.no_results')), 200);
}
// Direct user to appropriate status page.
if ($ldap_results['error']) {
return response()->json(Helper::formatStandardApiResponse('error', null, $ldap_results['error_message']), 200);
}
return response()->json(Helper::formatStandardApiResponse('success', null, $ldap_results['summary']), 200);
}
}

View File

@@ -2,9 +2,8 @@
namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use App\Models\Company;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
@@ -18,23 +17,29 @@ use \Illuminate\Http\RedirectResponse;
*
* @version v2.0
*/
class MaintenancesController extends Controller
class AssetMaintenancesController extends Controller
{
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the asset maintenances listing.
* the content for the asset maintenances listing, which is generated in getDatatable.
*
* @todo This should be replaced with middleware and/or policies
* @see AssetMaintenancesController::getDatatable() method that generates the JSON response
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
public function index() : View
{
$this->authorize('view', Asset::class);
return view('maintenances.index');
return view('asset_maintenances/index');
}
/**
* Returns a form view to create a new asset maintenance.
*
* @see MaintenancesController::postCreate() method that stores the data
* @see AssetMaintenancesController::postCreate() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
@@ -50,21 +55,21 @@ class MaintenancesController extends Controller
$asset->asset_id = $asset->id;
}
return view('maintenances/edit')
->with('maintenanceType', Maintenance::getImprovementOptions())
return view('asset_maintenances/edit')
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('asset', $asset)
->with('item', new Maintenance);
->with('item', new AssetMaintenance);
}
/**
* Validates and stores the new asset maintenance
*
* @see MaintenancesController::getCreate() method for the form
* @see AssetMaintenancesController::getCreate() method for the form
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
public function store(ImageUploadRequest $request) : RedirectResponse
public function store(Request $request) : RedirectResponse
{
$this->authorize('update', Asset::class);
@@ -73,73 +78,72 @@ class MaintenancesController extends Controller
// Loop through the selected assets
foreach ($assets as $asset) {
$maintenance = new Maintenance();
$maintenance->supplier_id = $request->input('supplier_id');
$maintenance->is_warranty = $request->input('is_warranty');
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$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
$maintenance->asset_id = $asset->id;
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$maintenance->name = $request->input('name');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
$maintenance->created_by = auth()->id();
$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 (($maintenance->completion_date !== null)
&& ($maintenance->start_date !== '')
&& ($maintenance->start_date !== '0000-00-00')
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($maintenance->start_date);
$completionDate = Carbon::parse($maintenance->completion_date);
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
$maintenance = $request->handleImages($maintenance);
// Was the asset maintenance created?
if (!$maintenance->save()) {
return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
if (!$assetMaintenance->save()) {
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
}
return redirect()->route('maintenances.index')
->with('success', trans('admin/maintenances/message.create.success'));
->with('success', trans('admin/asset_maintenances/message.create.success'));
}
/**
* Returns a form view to edit a selected asset maintenance.
*
* @see MaintenancesController::postEdit() method that stores the data
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
public function edit(Maintenance $maintenance) : View | RedirectResponse
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
return view('maintenances/edit')
return view('asset_maintenances/edit')
->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
->with('asset_ids', request()->input('asset_ids', []))
->with('maintenanceType', Maintenance::getImprovementOptions())
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('item', $maintenance);
}
/**
* Validates and stores an update to an asset maintenance
*
* @see MaintenancesController::postEdit() method that stores the data
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param Request $request
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
*/
public function update(ImageUploadRequest $request, Maintenance $maintenance) : View | RedirectResponse
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
@@ -149,7 +153,7 @@ class MaintenancesController extends Controller
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$maintenance->name = $request->input('name');
$maintenance->title = $request->input('title');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
@@ -172,11 +176,10 @@ class MaintenancesController extends Controller
$completionDate = Carbon::parse($maintenance->completion_date);
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
$maintenance = $request->handleImages($maintenance);
if ($maintenance->save()) {
return redirect()->route('maintenances.index')
->with('success', trans('admin/maintenances/message.edit.success'));
->with('success', trans('admin/asset_maintenances/message.edit.success'));
}
return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
@@ -186,11 +189,11 @@ class MaintenancesController extends Controller
* Delete an asset maintenance
*
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
*/
public function destroy(Maintenance $maintenance) : RedirectResponse
public function destroy(AssetMaintenance $maintenance) : RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
@@ -198,19 +201,19 @@ class MaintenancesController extends Controller
$maintenance->delete();
// Redirect to the asset_maintenance management page
return redirect()->route('maintenances.index')
->with('success', trans('admin/maintenances/message.delete.success'));
->with('success', trans('admin/asset_maintenances/message.delete.success'));
}
/**
* View an asset maintenance
*
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param int $maintenanceId
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
*/
public function show(Maintenance $maintenance) : View | RedirectResponse
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
{
return view('maintenances.view')->with('maintenance', $maintenance);
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
}
}

View File

@@ -87,20 +87,7 @@ class AssetModelsController extends Controller
$model->fieldset_id = $request->input('fieldset_id');
}
if ($request->has('use_cloned_image')) {
$cloned_model_img = AssetModel::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'models/'.$new_image_name;
Storage::disk('public')->copy('models/'.$cloned_model_img->image, $new_image);
$model->image = $new_image_name;
}
} else {
$model = $request->handleImages($model);
}
$model = $request->handleImages($model);
if ($model->save()) {
if ($this->shouldAddDefaultValues($request->input())) {
@@ -284,7 +271,7 @@ class AssetModelsController extends Controller
->with('depreciation_list', Helper::depreciationList())
->with('item', $model)
->with('model_id', $model->id)
->with('cloned_model', $cloned_model);
->with('clone_model', $cloned_model);
}

View File

@@ -0,0 +1,115 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\StorageHelper;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\AssetModel;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use \Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class AssetModelsFilesController extends Controller
{
/**
* Upload a file to the server.
*
* @param UploadFileRequest $request
* @param int $modelId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, $modelId = null) : RedirectResponse
{
if (! $model = AssetModel::find($modelId)) {
return redirect()->route('models.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('update', $model);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/assetmodels')) {
Storage::makeDirectory('private_uploads/assetmodels', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$model->id,$file);
$model->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
* Check for permissions and display the file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function show(AssetModel $model, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
{
$this->authorize('view', $model);
if (! $log = Actionlog::find($fileId)) {
return response('No matching record for that model/file', 500)
->header('Content-Type', 'text/plain');
}
$file = 'private_uploads/assetmodels/'.$log->filename;
if (! Storage::exists($file)) {
return response('File '.$file.' not found on server', 404)
->header('Content-Type', 'text/plain');
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
/**
* Delete the associated file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function destroy(AssetModel $model, $fileId = null) : RedirectResponse
{
$rel_path = 'private_uploads/assetmodels';
$this->authorize('update', $model);
$log = Actionlog::find($fileId);
if ($log) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace App\Http\Controllers\Assets;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Asset;
use \Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class AssetFilesController extends Controller
{
/**
* Upload a file to the server.
*
* @param UploadFileRequest $request
* @param int $assetId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, Asset $asset) : RedirectResponse
{
$this->authorize('update', $asset);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/assets')) {
Storage::makeDirectory('private_uploads/assets', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
$asset->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
}
return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
* Check for permissions and display the file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @param int $fileId
* @since [v1.0]
*/
public function show(Asset $asset, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
{
$this->authorize('view', $asset);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
$file = 'private_uploads/assets/'.$log->filename;
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('hardware.show', $asset)->with('error', trans('general.file_not_found'));
}
}
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
}
/**
* Delete the associated file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @param int $fileId
* @since [v1.0]
*/
public function destroy(Asset $asset, $fileId = null) : RedirectResponse
{
$this->authorize('update', $asset);
$rel_path = 'private_uploads/assets';
if ($log = Actionlog::find($fileId)) {
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
}
}

View File

@@ -157,16 +157,8 @@ class AssetsController extends Controller
$asset->location_id = $request->input('rtd_location_id', null);
}
if ($request->has('use_cloned_image')) {
$cloned_model_img = Asset::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'assets/'.$new_image_name;
Storage::disk('public')->copy('assets/'.$cloned_model_img->image, $new_image);
$asset->image = $new_image_name;
}
} else {
// Create the image (if one was chosen.)
if ($request->has('image')) {
$asset = $request->handleImages($asset);
}
@@ -234,13 +226,9 @@ class AssetsController extends Controller
$failures[] = join(",", $asset->getErrors()->all());
}
}
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
session()->put(['checkout_to_type' => $request->get('checkout_to_type'),
session()->put(['redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => 'model' ]);
@@ -424,9 +412,6 @@ class AssetsController extends Controller
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->element == 'checkbox' && !$request->has($field->db_column)) {
$asset->{$field->db_column} = null;
}
if ($request->has($field->db_column)) {
if ($field->field_encrypted == '1') {
if (Gate::allows('assets.view.encrypted_custom_fields')) {
@@ -659,9 +644,8 @@ class AssetsController extends Controller
*/
public function getClone(Asset $asset)
{
$this->authorize('create', Asset::class);
$this->authorize('create', $asset);
$cloned = clone $asset;
$cloned_model = $asset;
$cloned->id = null;
$cloned->asset_tag = '';
$cloned->serial = '';
@@ -671,7 +655,6 @@ class AssetsController extends Controller
return view('hardware/edit')
->with('statuslabel_list', Helper::statusLabelList())
->with('statuslabel_types', Helper::statusTypeList())
->with('cloned_model', $cloned_model)
->with('item', $cloned);
}

View File

@@ -544,7 +544,7 @@ class BulkAssetsController extends Controller
session()->put('bulk_asset_errors',$error_array);
return redirect()
->route('hardware.index')
->route('hardware.bulkedit')
->with('bulk_asset_errors', $error_array)
->withInput();
}

View File

@@ -88,12 +88,7 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return Helper::getRedirectOption($request, $component->id, 'Components')

View File

@@ -0,0 +1,138 @@
<?php
namespace App\Http\Controllers\Components;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Component;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\JsonResponse;
use Illuminate\Support\Facades\Log;
class ComponentsFilesController extends Controller
{
/**
* Validates and stores files associated with a component.
*
* @param UploadFileRequest $request
* @param int $componentId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @todo Switch to using the AssetFileRequest form request validator.
*/
public function store(UploadFileRequest $request, $componentId = null)
{
if (config('app.lock_passwords')) {
return redirect()->route('components.show', ['component'=>$componentId])->with('error', trans('general.feature_disabled'));
}
$component = Component::find($componentId);
if (isset($component->id)) {
$this->authorize('update', $component);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/components')) {
Storage::makeDirectory('private_uploads/components', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/components/','component-'.$component->id, $file);
//Log the upload to the log
$component->logUpload($file_name, e($request->input('notes')));
}
return redirect()->route('components.show', $component->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->route('components.show', $component->id)->with('error', trans('general.no_files_uploaded'));
}
// Prepare the error message
return redirect()->route('components.index')
->with('error', trans('general.file_does_not_exist'));
}
/**
* Deletes the selected component file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $componentId
* @param int $fileId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($componentId = null, $fileId = null)
{
$component = Component::find($componentId);
// the asset is valid
if (isset($component->id)) {
$this->authorize('update', $component);
$log = Actionlog::find($fileId);
// Remove the file if one exists
if (Storage::exists('components/'.$log->filename)) {
try {
Storage::delete('components/'.$log->filename);
} catch (\Exception $e) {
Log::debug($e);
}
}
$log->delete();
return redirect()->back()->withFragment('files')
->with('success', trans('admin/hardware/message.deletefile.success'));
}
// Redirect to the licence management page
return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist'));
}
/**
* Allows the selected file to be viewed.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.4]
* @param int $componentId
* @param int $fileId
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($componentId = null, $fileId = null)
{
Log::debug('Private filesystem is: '.config('filesystems.default'));
// the component is valid
if ($component = Component::find($componentId)) {
$this->authorize('view', $component);
$this->authorize('components.files', $component);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
$file = 'private_uploads/components/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found'));
}
}
return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
}
}

View File

@@ -7,7 +7,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Company;
use App\Models\Consumable;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
@@ -81,29 +81,13 @@ class ConsumablesController extends Controller
$consumable->purchase_date = $request->input('purchase_date');
$consumable->purchase_cost = $request->input('purchase_cost');
$consumable->qty = $request->input('qty');
$consumable->created_by = auth()->id();
$consumable->created_by = auth()->id();
$consumable->notes = $request->input('notes');
if ($request->has('use_cloned_image')) {
$cloned_model_img = Consumable::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'consumables/'.$new_image_name;
Storage::disk('public')->copy('consumables/'.$cloned_model_img->image, $new_image);
$consumable->image = $new_image_name;
}
} else {
$consumable = $request->handleImages($consumable);
}
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
$consumable = $request->handleImages($consumable);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
@@ -229,10 +213,9 @@ class ConsumablesController extends Controller
$consumable_to_close = $consumable;
$consumable = clone $consumable_to_close;
$consumable->id = null;
$consumable->image = null;
$consumable->created_by = null;
return view('consumables/edit')
->with('cloned_model', $consumable_to_close)
->with('item', $consumable);
return view('consumables/edit')->with('item', $consumable);
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace App\Http\Controllers\Consumables;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Consumable;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Storage;
use Symfony\Consumable\HttpFoundation\JsonResponse;
use Illuminate\Support\Facades\Log;
class ConsumablesFilesController extends Controller
{
/**
* Validates and stores files associated with a consumable.
*
* @param UploadFileRequest $request
* @param int $consumableId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @todo Switch to using the AssetFileRequest form request validator.
*/
public function store(UploadFileRequest $request, $consumableId = null)
{
if (config('app.lock_passwords')) {
return redirect()->route('consumables.show', ['consumable'=>$consumableId])->with('error', trans('general.feature_disabled'));
}
$consumable = Consumable::find($consumableId);
if (isset($consumable->id)) {
$this->authorize('update', $consumable);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/consumables')) {
Storage::makeDirectory('private_uploads/consumables', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/consumables/','consumable-'.$consumable->id, $file);
//Log the upload to the log
$consumable->logUpload($file_name, e($request->input('notes')));
}
return redirect()->route('consumables.show', $consumable->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->route('consumables.show', $consumable->id)->with('error', trans('general.no_files_uploaded'));
}
// Prepare the error message
return redirect()->route('consumables.index')
->with('error', trans('general.file_does_not_exist'));
}
/**
* Deletes the selected consumable file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $consumableId
* @param int $fileId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($consumableId = null, $fileId = null)
{
$consumable = Consumable::find($consumableId);
// the asset is valid
if (isset($consumable->id)) {
$this->authorize('update', $consumable);
$log = Actionlog::find($fileId);
// Remove the file if one exists
if (Storage::exists('consumables/'.$log->filename)) {
try {
Storage::delete('consumables/'.$log->filename);
} catch (\Exception $e) {
Log::debug($e);
}
}
$log->delete();
return redirect()->back()->withFragment('files')
->with('success', trans('admin/hardware/message.deletefile.success'));
}
// Redirect to the licence management page
return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist'));
}
/**
* Allows the selected file to be viewed.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.4]
* @param int $consumableId
* @param int $fileId
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($consumableId = null, $fileId = null)
{
$consumable = Consumable::find($consumableId);
// the consumable is valid
if (isset($consumable->id)) {
$this->authorize('view', $consumable);
$this->authorize('consumables.files', $consumable);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
$file = 'private_uploads/consumables/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found'));
}
}
// The log record doesn't exist somehow
return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
}
}

View File

@@ -22,15 +22,6 @@
namespace App\Http\Controllers;
use App\Models\Accessory;
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\Maintenance;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
@@ -41,45 +32,6 @@ abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
static $map_object_type = [
'accessories' => Accessory::class,
'maintenances' => Maintenance::class,
'assets' => Asset::class,
'components' => Component::class,
'consumables' => Consumable::class,
'hardware' => Asset::class,
'licenses' => License::class,
'locations' => Location::class,
'models' => AssetModel::class,
'users' => User::class,
];
static $map_storage_path = [
'accessories' => 'private_uploads/accessories/',
'maintenances' => 'private_uploads/maintenances/',
'assets' => 'private_uploads/assets/',
'components' => 'private_uploads/components/',
'consumables' => 'private_uploads/consumables/',
'hardware' => 'private_uploads/assets/',
'licenses' => 'private_uploads/licenses/',
'locations' => 'private_uploads/locations/',
'models' => 'private_uploads/models/',
'users' => 'private_uploads/users/',
];
static $map_file_prefix= [
'accessories' => 'accessory',
'maintenances' => 'maintenance',
'assets' => 'asset',
'components' => 'component',
'consumables' => 'consumable',
'hardware' => 'asset',
'licenses' => 'license',
'locations' => 'location',
'models' => 'model',
'users' => 'user',
];
public function __construct()
{
view()->share('signedIn', Auth::check());

View File

@@ -144,9 +144,10 @@ class CustomFieldsController extends Controller
*/
public function deleteFieldFromFieldset($field_id, $fieldset_id) : RedirectResponse
{
$this->authorize('update', CustomField::class);
$field = CustomField::find($field_id);
$this->authorize('update', $field);
// Check that the field exists - this is mostly related to the demo, where we
// rewrite the data every x minutes, so it's possible someone might be disassociating
// a field from a fieldset just as we're wiping the database
@@ -156,12 +157,11 @@ class CustomFieldsController extends Controller
return redirect()->route('fieldsets.show', ['fieldset' => $fieldset_id])
->with('success', trans('admin/custom_fields/message.field.delete.success'));
} else {
return redirect()->back()->with('error', trans('admin/custom_fields/message.field.delete.error'))
->withInput();
return redirect()->back()->withErrors(['message' => "Field is in use and cannot be deleted."]);
}
}
return redirect()->back()->with('error', trans('admin/custom_fields/message.field.delete.error'));
return redirect()->back()->withErrors(['message' => "Error deleting field from fieldset"]);
}
@@ -172,16 +172,20 @@ class CustomFieldsController extends Controller
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v1.8]
*/
public function destroy(CustomField $field) : RedirectResponse
public function destroy($field_id) : RedirectResponse
{
$this->authorize('delete', CustomField::class);
if ($field = CustomField::find($field_id)) {
$this->authorize('delete', $field);
if (($field->fieldset) && ($field->fieldset->count() > 0)) {
return redirect()->back()->with('error', trans('admin/custom_fields/message.field.delete.in_use'));
if (($field->fieldset) && ($field->fieldset->count() > 0)) {
return redirect()->back()->withErrors(['message' => 'Field is in-use']);
}
$field->delete();
return redirect()->route("fields.index")
->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
$field->delete();
return redirect()->route("fields.index")
->with("success", trans('admin/custom_fields/message.field.delete.success'));
return redirect()->back()->withErrors(['message' => 'Field does not exist']);
}
@@ -194,7 +198,7 @@ class CustomFieldsController extends Controller
*/
public function edit(Request $request, CustomField $field) : View | RedirectResponse
{
$this->authorize('update', CustomField::class);
$this->authorize('update', $field);
$fieldsets = CustomFieldset::get();
$customFormat = '';
if ((stripos($field->format, 'regex') === 0) && ($field->format !== CustomField::PREDEFINED_FORMATS['MAC'])) {
@@ -224,7 +228,7 @@ class CustomFieldsController extends Controller
*/
public function update(CustomFieldRequest $request, CustomField $field) : RedirectResponse
{
$this->authorize('update', CustomField::class);
$this->authorize('update', $field);
$show_in_email = $request->get("show_in_email", 0);
$display_in_user_view = $request->get("display_in_user_view", 0);
@@ -261,6 +265,7 @@ class CustomFieldsController extends Controller
if ($field->save()) {
// Sync fields with fieldsets
$fieldset_array = $request->input('associate_fieldsets');
if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) {

View File

@@ -87,9 +87,7 @@ class LicenseCheckinController extends Controller
if($licenseSeat->assigned_to != null){
$return_to = User::withTrashed()->find($licenseSeat->assigned_to);
if ($return_to) {
session()->put('checkedInFrom', $return_to->id);
}
session()->put('checkedInFrom', $return_to->id);
} else {
$return_to = Asset::find($licenseSeat->asset_id);
}

View File

@@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Licenses;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\License;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
class LicenseFilesController extends Controller
{
/**
* Validates and stores files associated with a license.
*
* @param UploadFileRequest $request
* @param int $licenseId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @todo Switch to using the AssetFileRequest form request validator.
*/
public function store(UploadFileRequest $request, $licenseId = null)
{
$license = License::find($licenseId);
if (isset($license->id)) {
$this->authorize('update', $license);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/licenses')) {
Storage::makeDirectory('private_uploads/licenses', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/licenses/','license-'.$license->id, $file);
//Log the upload to the log
$license->logUpload($file_name, e($request->input('notes')));
}
return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
}
return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
}
// Prepare the error message
return redirect()->route('licenses.index')
->with('error', trans('admin/licenses/message.does_not_exist'));
}
/**
* Deletes the selected license file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param int $licenseId
* @param int $fileId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($licenseId = null, $fileId = null)
{
if ($license = License::find($licenseId)) {
$this->authorize('update', $license);
if ($log = Actionlog::find($fileId)) {
// Remove the file if one exists
if (Storage::exists('licenses/'.$log->filename)) {
try {
Storage::delete('licenses/'.$log->filename);
} catch (\Exception $e) {
Log::debug($e);
}
}
$log->delete();
return redirect()->back()
->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->route('licenses.index')->with('error', trans('general.log_does_not_exist'));
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
}
/**
* Allows the selected file to be viewed.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.4]
* @param int $licenseId
* @param int $fileId
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show($licenseId = null, $fileId = null, $download = true)
{
$license = License::find($licenseId);
// the license is valid
if (isset($license->id)) {
$this->authorize('view', $license);
$this->authorize('licenses.files', $license);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
$file = 'private_uploads/licenses/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.file_not_found'));
}
}
// The log record doesn't exist somehow
return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found'));
}
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId]));
}
}

View File

@@ -102,11 +102,7 @@ class LicensesController extends Controller
$license->created_by = auth()->id();
$license->min_amt = $request->input('min_amt');
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return Helper::getRedirectOption($request, $license->id, 'Licenses')
@@ -308,16 +304,13 @@ class LicensesController extends Controller
$response = new StreamedResponse(function () {
// Open output stream
$handle = fopen('php://output', 'w');
$licenses = License::with('company',
$licenses= License::with('company',
'manufacturer',
'category',
'supplier',
'adminuser',
'assignedusers');
if (request()->filled('category_id')) {
$licenses = $licenses->where('category_id', request()->input('category_id'));
}
$licenses = $licenses->orderBy('created_at', 'DESC');
'assignedusers')
->orderBy('created_at', 'DESC');
Company::scopeCompanyables($licenses)
->chunk(500, function ($licenses) use ($handle) {
$headers = [

View File

@@ -96,18 +96,7 @@ class LocationsController extends Controller
$location->company_id = $request->input('company_id');
}
if ($request->has('use_cloned_image')) {
$cloned_model_img = Location::select('image')->find($request->input('clone_image_from_id'));
if ($cloned_model_img) {
$new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
$new_image = 'locations/'.$new_image_name;
Storage::disk('public')->copy('locations/'.$cloned_model_img->image, $new_image);
$location->image = $new_image_name;
}
} else {
$location = $request->handleImages($location);
}
$location = $request->handleImages($location);
if ($location->save()) {
return redirect()->route('locations.index')->with('success', trans('admin/locations/message.create.success'));
@@ -286,9 +275,9 @@ class LocationsController extends Controller
// unset these values
$location->id = null;
$location->image = null;
return view('locations/edit')
->with('cloned_model', $location_to_clone)
->with('item', $location);
}

View File

@@ -0,0 +1,111 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\StorageHelper;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use \Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class LocationsFilesController extends Controller
{
/**
* Upload a file to the server.
*
* @param UploadFileRequest $request
* @param int $modelId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, Location $location) : RedirectResponse
{
$this->authorize('update', $location);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/locations')) {
Storage::makeDirectory('private_uploads/locations', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/locations/','location-'.$location->id, $file);
$location->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
* Check for permissions and display the file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function show(Location $location, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
{
$this->authorize('view', $location);
if (! $log = Actionlog::find($fileId)) {
return redirect()->back()->withFragment('files')->with('error', 'No matching file record');
}
$file = 'private_uploads/locations/'.$log->filename;
if (! Storage::exists($file)) {
return redirect()->back()->withFragment('files')->with('error', 'No matching file on server');
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
/**
* Delete the associated file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function destroy(Location $location, $fileId = null) : RedirectResponse
{
$rel_path = 'private_uploads/locations';
$this->authorize('update', $location);
$log = Actionlog::find($fileId);
if ($log) {
// This should be moved to purge
// if (Storage::exists($rel_path.'/'.$log->filename)) {
// Storage::delete($rel_path.'/'.$log->filename);
// }
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
}

View File

@@ -51,7 +51,7 @@ class ManufacturersController extends Controller
$manufacturers_count = Manufacturer::withTrashed()->count();
if ($manufacturers_count == 0) {
Artisan::call('db:seed', ['--class' => 'Database\\Seeders\\ManufacturerSeeder', '--force' => true]);
Artisan::call('db:seed', ['--class' => 'ManufacturerSeeder']);
return redirect()->route('manufacturers.index')->with('success', trans('general.seeding.manufacturers.success'));
}

View File

@@ -9,7 +9,7 @@ use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\CustomField;
@@ -17,11 +17,13 @@ use App\Models\Depreciation;
use App\Models\License;
use App\Models\ReportTemplate;
use App\Models\Setting;
use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use \Illuminate\Contracts\View\View;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -1036,11 +1038,11 @@ class ReportsController extends Controller
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
*/
public function getMaintenancesReport() : View
public function getAssetMaintenancesReport() : View
{
$this->authorize('reports.view');
return view('reports.maintenances');
return view('reports.asset_maintenances');
}
/**
@@ -1049,11 +1051,11 @@ class ReportsController extends Controller
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
*/
public function exportMaintenancesReport() : Response
public function exportAssetMaintenancesReport() : Response
{
$this->authorize('reports.view');
// Grab all the improvements
$Maintenances = Maintenance::with('asset', 'supplier')
$assetMaintenances = AssetMaintenance::with('asset', 'supplier')
->orderBy('created_at', 'DESC')
->get();
@@ -1061,36 +1063,36 @@ class ReportsController extends Controller
$header = [
trans('admin/hardware/table.asset_tag'),
trans('admin/maintenances/table.asset_name'),
trans('admin/asset_maintenances/table.asset_name'),
trans('general.supplier'),
trans('admin/maintenances/form.asset_maintenance_type'),
trans('admin/maintenances/form.title'),
trans('admin/maintenances/form.start_date'),
trans('admin/maintenances/form.completion_date'),
trans('admin/maintenances/form.asset_maintenance_time'),
trans('admin/maintenances/form.cost'),
trans('admin/asset_maintenances/form.asset_maintenance_type'),
trans('admin/asset_maintenances/form.title'),
trans('admin/asset_maintenances/form.start_date'),
trans('admin/asset_maintenances/form.completion_date'),
trans('admin/asset_maintenances/form.asset_maintenance_time'),
trans('admin/asset_maintenances/form.cost'),
];
$header = array_map('trim', $header);
$rows[] = implode(',', $header);
foreach ($Maintenances as $maintenance) {
foreach ($assetMaintenances as $assetMaintenance) {
$row = [];
$row[] = str_replace(',', '', e($maintenance->asset->asset_tag));
$row[] = str_replace(',', '', e($maintenance->asset->name));
$row[] = str_replace(',', '', e($maintenance->supplier->name));
$row[] = e($maintenance->improvement_type);
$row[] = e($maintenance->name);
$row[] = e($maintenance->start_date);
$row[] = e($maintenance->completion_date);
if (is_null($maintenance->asset_maintenance_time)) {
$row[] = str_replace(',', '', e($assetMaintenance->asset->asset_tag));
$row[] = str_replace(',', '', e($assetMaintenance->asset->name));
$row[] = str_replace(',', '', e($assetMaintenance->supplier->name));
$row[] = e($assetMaintenance->improvement_type);
$row[] = e($assetMaintenance->title);
$row[] = e($assetMaintenance->start_date);
$row[] = e($assetMaintenance->completion_date);
if (is_null($assetMaintenance->asset_maintenance_time)) {
$improvementTime = (int) Carbon::now()
->diffInDays(Carbon::parse($maintenance->start_date), true);
->diffInDays(Carbon::parse($assetMaintenance->start_date), true);
} else {
$improvementTime = (int) $maintenance->asset_maintenance_time;
$improvementTime = (int) $assetMaintenance->asset_maintenance_time;
}
$row[] = $improvementTime;
$row[] = trans('general.currency') . Helper::formatCurrencyOutput($maintenance->cost);
$row[] = trans('general.currency') . Helper::formatCurrencyOutput($assetMaintenance->cost);
$rows[] = implode(',', $row);
}

View File

@@ -1084,7 +1084,6 @@ class SettingsController extends Controller
if (! config('app.lock_passwords')) {
if (Storage::exists($path.'/'.$filename)) {
Log::warning('User '.auth()->user()->username.' is attempting to download backup file: '.$filename);
return StorageHelper::downloader($path.'/'.$filename);
} else {
// Redirect to the backup page
@@ -1112,7 +1111,6 @@ class SettingsController extends Controller
if (Storage::exists($path . '/' . $filename)) {
try {
Log::warning('User '.auth()->user()->username.' is attempting to delete backup file: '.$filename);
Storage::delete($path . '/' . $filename);
return redirect()->route('settings.backups.index')->with('success', trans('admin/settings/message.backup.file_deleted'));
} catch (\Exception $e) {
@@ -1192,7 +1190,7 @@ class SettingsController extends Controller
'--force' => true,
]);
Log::warning('User '.auth()->user()->username.' is attempting to restore from: '. storage_path($path).'/'.$filename);
Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
$restore_params = [
'--force' => true,
@@ -1341,11 +1339,9 @@ class SettingsController extends Controller
'name' => config('mail.from.name'),
'email' => config('mail.from.address'),
])->notify(new MailTest());
Log::debug('Attempting to send mail to '.config('mail.from.address'));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('mail_sent.mail_sent')));
} catch (\Exception $e) {
Log::error('Mail sent from '.config('mail.from.address') .' with errors '. $e->getMessage());
Log::debug($e);
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Supplier;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
@@ -121,7 +122,7 @@ class SuppliersController extends Controller
public function destroy($supplierId) : RedirectResponse
{
$this->authorize('delete', Supplier::class);
if (is_null($supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->find($supplierId))) {
if (is_null($supplier = Supplier::with('asset_maintenances', 'assets', 'licenses')->withCount('asset_maintenances as asset_maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->find($supplierId))) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
}
@@ -129,8 +130,8 @@ class SuppliersController extends Controller
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
}
if ($supplier->maintenances_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count]));
if ($supplier->asset_maintenances_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count]));
}
if ($supplier->licenses_count > 0) {

View File

@@ -1,162 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\StorageHelper;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* This controller provide the health route for
* the Snipe-IT Asset Management application.
*
* @version v1.0
*
* @return \Illuminate\Http\JsonResponse
*/
class UploadedFilesController extends Controller
{
/**
* 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.2.2]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, $object_type, $id) : RedirectResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('update', $object);
if (!$object) {
return redirect()->back()->withFragment('files')->with('error',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 redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.upload.success', count($files)));
}
// No files were submitted
return redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.nofiles'));
}
/**
* Check for permissions and display the file.
* This isn't currently used, but is here for future use.
*
* @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 show from the action_logs table
* @since [v8.2.2]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function show($object_type, $id, $file_id) : RedirectResponse | 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 redirect()->back()->withFragment('files')->with('error',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 redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.invalid_id'));
}
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename))
{
return redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.file_not_found'));
}
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.2.2]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function destroy($object_type, $id, $file_id) : RedirectResponse
{
// 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 redirect()->back()->withFragment('files')->with('error',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 redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.delete.success', 1));
}
}
// The file doesn't seem to really exist, so report an error
return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.delete.error', 1));
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers\Users;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Illuminate\Support\Facades\Storage;
class UserFilesController extends Controller
{
/**
* Return JSON response with a list of user details for the getIndex() view.
*
* @param UploadFileRequest $request
* @param int $userId
* @return string JSON
* @throws \Illuminate\Auth\Access\AuthorizationException
*@author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.6]
*/
public function store(UploadFileRequest $request, User $user)
{
$this->authorize('update', $user);
$files = $request->file('file');
if (is_null($files)) {
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
}
foreach ($files as $file) {
$file_name = $request->handleFile('private_uploads/users/', 'user-'.$user->id, $file);
//Log the uploaded file to the log
$logAction = new Actionlog();
$logAction->item_id = $user->id;
$logAction->item_type = User::class;
$logAction->created_by = auth()->id();
$logAction->note = $request->input('notes');
$logAction->target_id = null;
$logAction->created_at = date("Y-m-d H:i:s");
$logAction->filename = $file_name;
$logAction->action_type = 'uploaded';
if (! $logAction->save()) {
return JsonResponse::create(['error' => 'Failed validation: '.print_r($logAction->getErrors(), true)], 500);
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
}
}
/**
* Delete file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.6]
* @param int $userId
* @param int $fileId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function destroy($userId = null, $fileId = null)
{
if ($user = User::find($userId)) {
$this->authorize('delete', $user);
$rel_path = 'private_uploads/users';
if ($log = Actionlog::find($fileId)) {
$filename = $log->filename;
$log->delete();
if (Storage::exists($rel_path.'/'.$filename)) {
Storage::delete($rel_path.'/'.$filename);
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.deletefile.success'));
}
}
// The log record doesn't exist somehow
return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
}
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
}
/**
* Display/download the uploaded file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.6]
* @param int $userId
* @param int $fileId
* @return mixed
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function show(User $user, $fileId = null)
{
if (empty($fileId)) {
return redirect()->route('users.show')->with('error', 'Invalid file request');
}
$this->authorize('view', $user);
if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
$file = 'private_uploads/users/'.$log->filename;
try {
return StorageHelper::showOrDownloadFile($file, $log->filename);
} catch (\Exception $e) {
return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found'));
}
}
// The log record doesn't exist somehow
return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.log_record_not_found'));
}
}

View File

@@ -98,7 +98,6 @@ class UsersController extends Controller
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle');
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->department_id = $request->input('department_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
@@ -127,12 +126,7 @@ class UsersController extends Controller
// we have to invoke the form request here to handle image uploads
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
if($request->get('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
}
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($user->save()) {
if ($request->filled('groups')) {
@@ -245,7 +239,6 @@ class UsersController extends Controller
$user->employee_num = $request->input('employee_num');
$user->jobtitle = $request->input('jobtitle', null);
$user->phone = $request->input('phone');
$user->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
@@ -439,7 +432,7 @@ class UsersController extends Controller
app('request')->request->set('permissions', $permissions);
$user_to_clone = User::with('userloc')->withTrashed()->find($user->id);
$user_to_clone = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($user->id);
// Make sure they can view this particular user
$this->authorize('view', $user_to_clone);
@@ -454,8 +447,6 @@ class UsersController extends Controller
$user->last_name = '';
$user->email = substr($user->email, ($pos = strpos($user->email, '@')) !== false ? $pos : 0);
$user->id = null;
$user->username = null;
$user->avatar = null;
// Get this user's groups
$userGroups = $user_to_clone->groups()->pluck('name', 'id');
@@ -471,7 +462,7 @@ class UsersController extends Controller
->with('user', $user)
->with('groups', Group::pluck('name', 'id'))
->with('userGroups', $userGroups)
->with('cloned_model', $user_to_clone)
->with('clone_user', $user_to_clone)
->with('item', $user);
}

View File

@@ -26,6 +26,7 @@ class SecurityHeaders
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
// Ugh. Feature-Policy is dumb and clumsy and mostly irrelevant for Snipe-IT,
// since we don't provide any way to IFRAME anything in in the first place.

View File

@@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Storage;
use Intervention\Image\Exception\NotReadableException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class ImageUploadRequest extends Request
{
@@ -71,25 +70,19 @@ class ImageUploadRequest extends Request
public function handleImages($item, $w = 600, $form_fieldname = 'image', $path = null, $db_fieldname = 'image')
{
$type = class_basename(get_class($item));
$type = strtolower(class_basename(get_class($item)));
if (is_null($path)) {
$path = strtolower(str_plural($type));
$path = str_plural($type);
if ($type == 'AssetModel') {
if ($type == 'assetmodel') {
$path = 'models';
}
if ($type == 'user') {
$path = 'avatars';
}
}
if (!Storage::disk('public')->exists($path)) {
Storage::disk('public')->makeDirectory($path);
}
if ($this->offsetGet($form_fieldname) instanceof UploadedFile) {
@@ -100,9 +93,10 @@ class ImageUploadRequest extends Request
if (isset($image)) {
if (!config('app.lock_passwords')) {
$ext = $image->guessExtension();
$file_name = $type.'-'.$form_fieldname.($item->id ?? '-'.$item->id).'-'.str_random(10).'.'.$ext;
$file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
if (($image->getMimeType() == 'image/vnd.microsoft.icon') || ($image->getMimeType() == 'image/x-icon') || ($image->getMimeType() == 'image/avif') || ($image->getMimeType() == 'image/webp')) {
// If the file is an icon, webp or avif, we need to just move it since gd doesn't support resizing
@@ -144,7 +138,7 @@ class ImageUploadRequest extends Request
// Remove Current image if exists
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
$item->{$db_fieldname} = $file_name;
}
// If the user isn't uploading anything new but wants to delete their old image, do so

View File

@@ -2,7 +2,6 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CustomField;
@@ -17,7 +16,6 @@ use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class ActionlogsTransformer
{
@@ -135,6 +133,24 @@ class ActionlogsTransformer
$clean_meta= $this->changedInfo($clean_meta);
}
$file_url = '';
if($actionlog->filename!='') {
if ($actionlog->action_type == 'accepted') {
$file_url = route('log.storedeula.download', ['filename' => $actionlog->filename]);
} else {
if ($actionlog->item) {
if ($actionlog->itemType() == 'asset') {
$file_url = route('show/assetfile', ['asset' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'accessory') {
$file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'license') {
$file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'user') {
$file_url = route('show/userfile', ['user' => $actionlog->item->id, 'fileId' => $actionlog->id]);
}
}
}
}
$array = [
'id' => (int) $actionlog->id,
@@ -142,10 +158,8 @@ class ActionlogsTransformer
'file' => ($actionlog->filename!='')
?
[
'url' => $actionlog->uploads_file_url(),
'url' => $file_url,
'filename' => $actionlog->filename,
'inlineable' => StorageHelper::allowSafeInline($actionlog->uploads_file_url()),
'exists_on_disk' => Storage::exists($actionlog->uploads_file_path()) ? true : false,
] : null,
'item' => ($actionlog->item) ? [

View File

@@ -4,24 +4,23 @@ namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Storage;
class MaintenancesTransformer
class AssetMaintenancesTransformer
{
public function transformMaintenances(Collection $maintenances, $total)
public function transformAssetMaintenances(Collection $assetmaintenances, $total)
{
$array = [];
foreach ($maintenances as $assetmaintenance) {
$array[] = self::transformMaintenance($assetmaintenance);
foreach ($assetmaintenances as $assetmaintenance) {
$array[] = self::transformAssetMaintenance($assetmaintenance);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformMaintenance(Maintenance $assetmaintenance)
public function transformAssetMaintenance(AssetMaintenance $assetmaintenance)
{
$array = [
'id' => (int) $assetmaintenance->id,
@@ -34,7 +33,6 @@ class MaintenancesTransformer
'created_at' => Helper::getFormattedDateObject($assetmaintenance->asset->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->asset->updated_at, 'datetime'),
] : null,
'image' => ($assetmaintenance->image != '') ? Storage::disk('public')->url('maintenances/'.e($assetmaintenance->image)) : null,
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
'id' => (int) $assetmaintenance->asset->model->id,
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
@@ -50,8 +48,7 @@ class MaintenancesTransformer
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
] : null,
'name' => ($assetmaintenance->name) ? e($assetmaintenance->name) : null,
'title' => ($assetmaintenance->name) ? e($assetmaintenance->name) : null, // legacy to not change the shape of the API
'title' => ($assetmaintenance->title) ? e($assetmaintenance->title) : null,
'location' => (($assetmaintenance->asset) && ($assetmaintenance->asset->location)) ? [
'id' => (int) $assetmaintenance->asset->location->id,
'name'=> e($assetmaintenance->asset->location->name),

View File

@@ -58,13 +58,6 @@ class AssetsTransformer
'id' => (int) $asset->model->manufacturer->id,
'name'=> e($asset->model->manufacturer->name),
] : null,
'depreciation' => (($asset->model) && ($asset->model->depreciation)) ? [
'id' => (int) $asset->model->depreciation->id,
'name'=> e($asset->model->depreciation->name),
'months'=> (int) $asset->model->depreciation->months,
'type'=> e($asset->model->depreciation->depreciation_type),
'minimum'=> ($asset->model->depreciation->depreciation_min) ? (int) $asset->model->depreciation->depreciation_min : null,
] : null,
'supplier' => ($asset->supplier) ? [
'id' => (int) $asset->supplier->id,
'name'=> e($asset->supplier->name),

View File

@@ -25,7 +25,7 @@ class ConsumablesTransformer
$array = [
'id' => (int) $consumable->id,
'name' => e($consumable->name),
'image' => ($consumable->getImageUrl()) ? ($consumable->getImageUrl()) : null,
'image' => ($consumable->image) ? Storage::disk('public')->url('consumables/'.e($consumable->image)) : null,
'category' => ($consumable->category) ? ['id' => $consumable->category->id, 'name' => e($consumable->category->name)] : null,
'company' => ($consumable->company) ? ['id' => (int) $consumable->company->id, 'name' => e($consumable->company->name)] : null,
'item_no' => e($consumable->item_no),

View File

@@ -62,7 +62,7 @@ class LicensesTransformer
'checkin' => Gate::allows('checkin', License::class),
'clone' => Gate::allows('create', License::class),
'update' => Gate::allows('update', License::class),
'delete' => (Gate::allows('delete', License::class) && ($license->free_seats_count == $license->seats)) ? true : false,
'delete' => (Gate::allows('delete', License::class) && ($license->free_seats_count > 0)) ? true : false,
];
$array += $permissions_array;

View File

@@ -32,11 +32,10 @@ class UploadedFilesTransformer
'name' => e($file->filename),
'item' => ($file->item_type) ? [
'id' => (int) $file->item_id,
'type' => str_plural(strtolower(class_basename($file->item_type))),
'type' => strtolower(class_basename($file->item_type)),
] : null,
'filename' => e($file->filename),
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
'mediatype' => StorageHelper::getMediaType($file->uploads_file_path()),
'url' => $file->uploads_file_url(),
'note' => ($file->note) ? e($file->note) : null,
'created_by' => ($file->adminuser) ? [
@@ -45,7 +44,7 @@ class UploadedFilesTransformer
] : null,
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
'inlineable' => StorageHelper::allowSafeInline($file->uploads_file_path()) ?? false,
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
];

View File

@@ -22,12 +22,6 @@ class UsersTransformer
public function transformUser(User $user)
{
$role = '';
if ($user->isSuperUser()) {
$role = 'superadmin';
} elseif ($user->isAdmin()) {
$role = 'admin';
}
$array = [
'id' => (int) $user->id,
'avatar' => e($user->present()->gravatar) ?? null,
@@ -45,7 +39,6 @@ class UsersTransformer
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'vip' => ($user->vip == '1') ? true : false,
'phone' => ($user->phone) ? e($user->phone) : null,
'mobile' => ($user->mobile) ? e($user->mobile) : null,
'website' => ($user->website) ? e($user->website) : null,
'address' => ($user->address) ? e($user->address) : null,
'city' => ($user->city) ? e($user->city) : null,
@@ -66,7 +59,6 @@ class UsersTransformer
'name'=> e($user->userloc->name),
] : null,
'notes'=> Helper::parseEscapedMarkedownInline($user->notes),
'role' => $role,
'permissions' => $user->decodePermissions(),
'activated' => ($user->activated == '1') ? true : false,
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,

View File

@@ -52,7 +52,6 @@ class UserImporter extends ItemImporter
$this->item['email'] = trim($this->findCsvMatch($row, 'email'));
$this->item['gravatar'] = trim($this->findCsvMatch($row, 'gravatar'));
$this->item['phone'] = trim($this->findCsvMatch($row, 'phone_number'));
$this->item['mobile'] = trim($this->findCsvMatch($row, 'mobile_number'));
$this->item['website'] = trim($this->findCsvMatch($row, 'website'));
$this->item['jobtitle'] = trim($this->findCsvMatch($row, 'jobtitle'));
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));

View File

@@ -334,7 +334,6 @@ class Importer extends Component
'manager_username' => trans('general.importer.manager_username'),
'notes' => trans('general.notes'),
'phone_number' => trans('admin/users/table.phone'),
'mobile_number' => trans('admin/users/table.mobile'),
'remote' => trans('admin/users/general.remote'),
'start_date' => trans('general.start_date'),
'state' => trans('general.state'),
@@ -511,13 +510,6 @@ class Importer extends Component
'telephone',
'tel.',
],
'mobile_number' =>
[
'mobile',
'mobile number',
'cell',
'cellphone',
],
'serial' =>
[

View File

@@ -47,7 +47,7 @@ class CheckinAssetMail extends Mailable
return new Envelope(
from: $from,
subject: trans('mail.Asset_Checkin_Notification', ['tag' => $this->item->asset_tag]),
subject: trans('mail.Asset_Checkin_Notification'),
);
}

View File

@@ -24,25 +24,16 @@ class CheckoutAssetMail extends Mailable
/**
* Create a new message instance.
* @throws \Exception
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
$this->target = $checkedOutTo;
// Location is a target option, but there are no emails currently associated with locations.
if($this->target instanceof User){
$this->target = $this->target->present()?->fullName();
}
else if($this->target instanceof Asset){
$this->target = $this->target->assignedto?->present()?->fullName();
}
$this->last_checkout = '';
$this->expected_checkin = '';
@@ -125,7 +116,7 @@ class CheckoutAssetMail extends Mailable
private function getSubject(): string
{
if ($this->firstTimeSending) {
return trans('mail.Asset_Checkout_Notification', ['tag' => $this->item->asset_tag]);
return trans('mail.Asset_Checkout_Notification');
}
return trans('mail.unaccepted_asset_reminder');

View File

@@ -2,7 +2,6 @@
namespace App\Mail;
use App\Models\Asset;
use App\Models\LicenseSeat;
use App\Models\Setting;
use App\Models\User;
@@ -26,16 +25,9 @@ class CheckoutLicenseMail extends Mailable
$this->item = $licenseSeat;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
$this->target = $checkedOutTo;
if($this->target instanceof User){
$this->target = $this->target->present()?->fullName();
}
elseif($this->target instanceof Asset){
$this->target = $this->target->assignedto?->present()?->fullName();
}
}
/**

View File

@@ -7,7 +7,6 @@ use App\Presenters\Presentable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
/**
* Model for the Actionlog (the table that keeps a historical log of
@@ -458,40 +457,38 @@ class Actionlog extends SnipeModel
public function uploads_file_url()
{
if (($this->action_type == 'accepted') || ($this->action_type == 'declined')) {
return route('log.storedeula.download', ['filename' => $this->filename]);
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;
}
$object = Str::snake(str_plural(str_replace("App\Models\\", '', $this->item_type)));
if ($object == 'asset_models') {
$object = 'models';
}
return route('ui.files.show', [
'object_type' => $object,
'id' => $this->item_id,
'file_id' => $this->id,
]);
}
public function uploads_file_path()
{
if (($this->action_type == 'accepted') || ($this->action_type == 'declined')) {
return 'private_uploads/eula-pdfs/'.$this->filename;
}
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/models/'.$this->filename;
return 'private_uploads/assetmodels/'.$this->filename;
case Consumable::class:
return 'private_uploads/consumables/'.$this->filename;
case Component::class:
@@ -500,8 +497,6 @@ class Actionlog extends SnipeModel
return 'private_uploads/licenses/'.$this->filename;
case Location::class:
return 'private_uploads/locations/'.$this->filename;
case Maintenance::class:
return 'private_uploads/maintenances/'.$this->filename;
case User::class:
return 'private_uploads/users/'.$this->filename;
default:
@@ -510,6 +505,11 @@ class Actionlog extends SnipeModel
}
// Manually sets $this->source for determineActionSource()
public function setActionSource($source = null): void
{

View File

@@ -112,7 +112,7 @@ class Asset extends Depreciable
'location_id' => ['nullable', 'exists:locations,id', 'fmcs_location'],
'rtd_location_id' => ['nullable', 'exists:locations,id', 'fmcs_location'],
'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'],
'serial' => ['nullable', 'string', 'unique_undeleted:assets,serial'],
'serial' => ['nullable', 'unique_undeleted:assets,serial'],
'purchase_cost' => ['nullable', 'numeric', 'gte:0', 'max:9999999999999'],
'supplier_id' => ['nullable', 'exists:suppliers,id'],
'asset_eol_date' => ['nullable', 'date'],
@@ -206,17 +206,6 @@ class Asset extends Depreciable
'model.manufacturer' => ['name'],
];
protected static function booted(): void
{
static::forceDeleted(function (Asset $asset) {
$asset->requests()->forceDelete();
});
static::softDeleted(function (Asset $asset) {
$asset->requests()->delete();
});
}
// To properly set the expected checkin as Y-m-d
public function setExpectedCheckinAttribute($value)
{
@@ -237,11 +226,7 @@ class Asset extends Depreciable
foreach ($this->model->fieldset->fields as $field) {
// this just casts booleans that may come through as strings to an actual boolean type
// adding !$field->field_encrypted because when the encrypted value comes through it
// screws things up for the encrypted validation rules (and the encrypted string
// is not a valid boolean type)
if ($field->format == 'BOOLEAN' && !$field->field_encrypted) {
if ($field->format == 'BOOLEAN') {
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
@@ -771,9 +756,9 @@ class Asset extends Depreciable
* @since 1.0
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function maintenances()
public function assetmaintenances()
{
return $this->hasMany(\App\Models\Maintenance::class, 'asset_id')
return $this->hasMany(\App\Models\AssetMaintenance::class, 'asset_id')
->orderBy('created_at', 'desc');
}

View File

@@ -4,34 +4,31 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use App\Models\Traits\HasUploads;
/**
* Model for Asset Maintenances.
*
* @version v1.0
*/
class Maintenance extends SnipeModel implements ICompanyableChild
class AssetMaintenance extends Model implements ICompanyableChild
{
use HasFactory;
use HasUploads;
use SoftDeletes;
use CompanyableChildTrait;
use ValidatingTrait;
use Loggable, Presentable;
protected $table = 'maintenances';
protected $table = 'asset_maintenances';
protected $rules = [
'asset_id' => 'required|integer',
'supplier_id' => 'nullable|integer',
'asset_maintenance_type' => 'required',
'name' => 'required|max:100',
'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',
@@ -46,7 +43,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
* @var array
*/
protected $fillable = [
'name',
'title',
'asset_id',
'supplier_id',
'asset_maintenance_type',
@@ -67,7 +64,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
protected $searchableAttributes =
[
'name',
'title',
'notes',
'asset_maintenance_type',
'cost',
@@ -103,14 +100,14 @@ class Maintenance extends SnipeModel implements ICompanyableChild
public static function getImprovementOptions()
{
return [
trans('admin/maintenances/general.maintenance') => trans('admin/maintenances/general.maintenance'),
trans('admin/maintenances/general.repair') => trans('admin/maintenances/general.repair'),
trans('admin/maintenances/general.upgrade') => trans('admin/maintenances/general.upgrade'),
trans('admin/maintenances/general.pat_test') => trans('admin/maintenances/general.pat_test'),
trans('admin/maintenances/general.calibration') => trans('admin/maintenances/general.calibration'),
trans('admin/maintenances/general.software_support') => trans('admin/maintenances/general.software_support'),
trans('admin/maintenances/general.hardware_support') => trans('admin/maintenances/general.hardware_support'),
trans('admin/maintenances/general.configuration_change') => trans('admin/maintenances/general.configuration_change'),
trans('admin/asset_maintenances/general.maintenance') => trans('admin/asset_maintenances/general.maintenance'),
trans('admin/asset_maintenances/general.repair') => trans('admin/asset_maintenances/general.repair'),
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade'),
trans('admin/asset_maintenances/general.pat_test') => trans('admin/asset_maintenances/general.pat_test'),
trans('admin/asset_maintenances/general.calibration') => trans('admin/asset_maintenances/general.calibration'),
trans('admin/asset_maintenances/general.software_support') => trans('admin/asset_maintenances/general.software_support'),
trans('admin/asset_maintenances/general.hardware_support') => trans('admin/asset_maintenances/general.hardware_support'),
trans('admin/asset_maintenances/general.configuration_change') => trans('admin/asset_maintenances/general.configuration_change'),
];
}
@@ -169,21 +166,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
return $this->belongsTo(\App\Models\Asset::class, 'asset_id')
->withTrashed();
}
/**
* Get the maintenance logs
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v8.2.2]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
{
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
->where('item_type', '=', self::class)
->orderBy('created_at', 'desc')
->withTrashed();
}
/**
@@ -205,11 +187,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
->withTrashed();
}
public function getDisplayNameAttribute()
{
return $this->name;
}
/**
* -----------------------------------------------
* BEGIN QUERY SCOPES
@@ -226,7 +203,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderBySupplier($query, $order)
{
return $query->leftJoin('suppliers as suppliers_maintenances', 'maintenances.supplier_id', '=', 'suppliers_maintenances.id')
return $query->leftJoin('suppliers as suppliers_maintenances', 'asset_maintenances.supplier_id', '=', 'suppliers_maintenances.id')
->orderBy('suppliers_maintenances.name', $order);
}
@@ -242,7 +219,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderByTag($query, $order)
{
return $query->leftJoin('assets', 'maintenances.asset_id', '=', 'assets.id')
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
->orderBy('assets.asset_tag', $order);
}
@@ -256,7 +233,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderByAssetName($query, $order)
{
return $query->leftJoin('assets', 'maintenances.asset_id', '=', 'assets.id')
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
->orderBy('assets.name', $order);
}
@@ -270,7 +247,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderByAssetSerial($query, $order)
{
return $query->leftJoin('assets', 'maintenances.asset_id', '=', 'assets.id')
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
->orderBy('assets.serial', $order);
}
@@ -284,7 +261,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderStatusName($query, $order)
{
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
->leftjoin('status_labels as maintained_asset_status', 'maintained_asset_status.id', '=', 'maintained_asset.status_id')
->orderBy('maintained_asset_status.name', $order);
}
@@ -299,7 +276,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderLocationName($query, $order)
{
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
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);
}
@@ -309,6 +286,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
*/
public function scopeOrderByCreatedBy($query, $order)
{
return $query->leftJoin('users as admin_sort', 'maintenances.created_by', '=', 'admin_sort.id')->select('maintenances.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
return $query->leftJoin('users as admin_sort', 'asset_maintenances.created_by', '=', 'admin_sort.id')->select('asset_maintenances.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
}
}

View File

@@ -98,16 +98,8 @@ class AssetModel extends SnipeModel
'manufacturer' => ['name'],
];
protected static function booted(): void
{
static::forceDeleted(function (AssetModel $assetModel) {
$assetModel->requests()->forceDelete();
});
static::softDeleted(function (AssetModel $assetModel) {
$assetModel->requests()->delete();
});
}
/**
* Establishes the model -> assets relationship

View File

@@ -2,13 +2,11 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class CheckoutRequest extends Model
{
use HasFactory;
use SoftDeletes;
protected $fillable = ['user_id'];
protected $table = 'checkout_requests';

View File

@@ -230,16 +230,11 @@ class Consumable extends SnipeModel
*/
public function getImageUrl()
{
// If there is a consumable image, use that
if ($this->image) {
return Storage::disk('public')->url(app('consumables_upload_path').$this->image);
// Otherwise check for a category image
} elseif (($this->category) && ($this->category->image)) {
return Storage::disk('public')->url(app('categories_upload_path').e($this->category->image));
}
return false;
}
/**

View File

@@ -16,7 +16,6 @@ class CustomField extends Model
UniqueUndeletedTrait;
/**
*
* Custom field predfined formats
*
* @var array

View File

@@ -3,19 +3,12 @@
namespace App\Models;
use App\Rules\AlphaEncrypted;
use App\Rules\BooleanEncrypted;
use App\Rules\DateEncrypted;
use App\Rules\EmailEncrypted;
use App\Rules\IPEncrypted;
use App\Rules\IPv4Encrypted;
use App\Rules\IPv6Encrypted;
use App\Rules\MacEncrypted;
use App\Rules\NumericEncrypted;
use App\Rules\RegexEncrypted;
use App\Rules\UrlEncrypted;
use Gate;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use Watson\Validating\ValidatingTrait;
class CustomFieldset extends Model
@@ -106,7 +99,7 @@ class CustomFieldset extends Model
* @since [v3.0]
* @return array
*/
public function validation_rules(): array
public function validation_rules()
{
$rules = [];
foreach ($this->fields as $field) {
@@ -128,65 +121,18 @@ class CustomFieldset extends Model
$rules[$field->db_column_name()] = $rule;
// this is to switch the rules to rules specially made for encrypted custom fields that decrypt the value before validating
if ($field->field_encrypted) {
$numericKey = array_search('numeric', $rules[$field->db_column_name()]);
$alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
$emailKey = array_search('email', $rules[$field->db_column_name()]);
$dateKey = array_search('date', $rules[$field->db_column_name()]);
$urlKey = array_search('url', $rules[$field->db_column_name()]);
$ipKey = array_search('ip', $rules[$field->db_column_name()]);
$ipv4Key = array_search('ipv4', $rules[$field->db_column_name()]);
$ipv6Key = array_search('ipv6', $rules[$field->db_column_name()]);
$macKey = array_search('regex:/^[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}$/', $rules[$field->db_column_name()]);
$booleanKey = array_search('boolean', $rules[$field->db_column_name()]);
// find objects in array that start with "regex:"
// collect because i couldn't figure how to do this
// with array filter and get keys out of it
$regexCollect = collect($rules[$field->db_column_name()]);
$regexKeys = $regexCollect->filter(function ($value, $key) {
return starts_with($value, 'regex:');
})->keys()->values()->toArray();
switch ($field->format) {
case 'NUMERIC':
$rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
break;
case 'ALPHA':
$rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
break;
case 'EMAIL':
$rules[$field->db_column_name()][$emailKey] = new EmailEncrypted;
break;
case 'DATE':
$rules[$field->db_column_name()][$dateKey] = new DateEncrypted;
break;
case 'URL':
$rules[$field->db_column_name()][$urlKey] = new UrlEncrypted;
break;
case 'IP':
$rules[$field->db_column_name()][$ipKey] = new IPEncrypted;
break;
case 'IPV4':
$rules[$field->db_column_name()][$ipv4Key] = new IPv4Encrypted;
break;
case 'IPV6':
$rules[$field->db_column_name()][$ipv6Key] = new IPv6Encrypted;
break;
case 'MAC':
$rules[$field->db_column_name()][$macKey] = new MacEncrypted;
break;
case 'BOOLEAN':
$rules[$field->db_column_name()][$booleanKey] = new BooleanEncrypted;
break;
case starts_with($field->format, 'regex'):
foreach ($regexKeys as $regexKey) {
$rules[$field->db_column_name()][$regexKey] = new RegexEncrypted;
}
break;
}
// these are to replace the standard 'numeric' and 'alpha' rules if the custom field is also encrypted.
// the values need to be decrypted first, because encrypted strings are alphanumeric
if ($field->format === 'NUMERIC' && $field->field_encrypted) {
$numericKey = array_search('numeric', $rules[$field->db_column_name()]);
$rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
}
if ($field->format === 'ALPHA' && $field->field_encrypted) {
$alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
$rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
}
// add not_array to rules for all fields but checkboxes
if ($field->element != 'checkbox') {

View File

@@ -1,116 +0,0 @@
<?php
namespace App\Models\Labels\Tapes\Dymo;
class LabelWriter_11354 extends LabelWriter
{
private const BARCODE1D_HEIGHT = 3.00;
private const BARCODE_MARGIN = 1.80;
private const TAG_SIZE = 2.80;
private const TITLE_SIZE = 2.80;
private const TITLE_MARGIN = 0.50;
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 57;
}
public function getHeight()
{
return 32;
}
public function getSupportAssetTag()
{
return true;
}
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)
{
}
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$usableWidth = $pa->w;
$usableHeight = $pa->h;
// Wide 1D barcode on top
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
$currentX, $currentY, $usableWidth, self::BARCODE1D_HEIGHT
);
$currentY += self::BARCODE1D_HEIGHT + self::BARCODE_MARGIN;
$usableHeight -= self::BARCODE1D_HEIGHT + self::BARCODE_MARGIN;
}
// 2D Barcode in left column
if ($record->has('barcode2d')) {
$barcodeSize = $usableHeight - self::TAG_SIZE;
static::writeText(
$pdf, $record->get('tag'),
$currentX, $pa->y2 - self::TAG_SIZE,
'freesans', 'b', self::TAG_SIZE, 'C',
$barcodeSize, self::TAG_SIZE, true, 0
);
static::write2DBarcode(
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
$currentX, $currentY,
$barcodeSize, $barcodeSize
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
// Right column
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'b', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$currentX, $currentY,
'freesans', '', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.3
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
}
}
}

View File

@@ -21,7 +21,7 @@ class SnipeModel extends Model
*/
public function setPurchaseCostAttribute($value)
{
if (is_numeric($value)) {
if (is_float($value)) {
//value is *already* a floating-point number. Just assign it directly
$this->attributes['purchase_cost'] = $value;
return;

View File

@@ -153,21 +153,12 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig
"primary" => AttributeMapping::constant(true)->ignoreWrite()
]],
// Mobile and work phone numbers
'phoneNumbers' => [
[
"value" => AttributeMapping::eloquent("phone"),
"display" => null,
"type" => AttributeMapping::constant("work")->ignoreWrite(),
"primary" => AttributeMapping::constant(true)->ignoreWrite(),
],
[
"value" => AttributeMapping::eloquent("mobile"),
"display" => null,
"type" => AttributeMapping::constant("mobile")->ignoreWrite(),
"primary" => AttributeMapping::constant(false)->ignoreWrite()
]
],
'phoneNumbers' => [[
"value" => AttributeMapping::eloquent("phone"),
"display" => null,
"type" => AttributeMapping::constant("work")->ignoreWrite(),
"primary" => AttributeMapping::constant(true)->ignoreWrite()
]],
'ims' => [[
"value" => null,

View File

@@ -7,7 +7,7 @@ use App\Models\Traits\Searchable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use \Illuminate\Database\Eloquent\Relations\Relation;
class Supplier extends SnipeModel
{
use HasFactory;
@@ -133,7 +133,7 @@ class Supplier extends SnipeModel
* Establishes the supplier -> admin user relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @return Relation
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
{
@@ -147,9 +147,9 @@ class Supplier extends SnipeModel
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function maintenances(): Relation
public function asset_maintenances()
{
return $this->hasMany(\App\Models\Maintenance::class, 'supplier_id');
return $this->hasMany(\App\Models\AssetMaintenance::class, 'supplier_id');
}
/**

View File

@@ -70,7 +70,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
'manager_id',
'password',
'phone',
'mobile',
'notes',
'state',
'username',
@@ -140,7 +139,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
'locale',
'notes',
'phone',
'mobile',
'state',
'username',
'website',
@@ -185,17 +183,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
);
}
protected static function booted(): void
{
static::forceDeleted(function (User $user) {
CheckoutRequest::where(['user_id' => $user->id])->forceDelete();
});
static::softDeleted(function (User $user) {
CheckoutRequest::where(['user_id' => $user->id])->delete();
});
}
public function isAvatarExternal()
{
@@ -411,9 +398,9 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
* @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function maintenances()
public function assetmaintenances()
{
return $this->hasMany(\App\Models\Maintenance::class, 'user_id')->withTrashed();
return $this->hasMany(\App\Models\AssetMaintenance::class, 'user_id')->withTrashed();
}
/**
@@ -904,49 +891,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
);
}
/**
* Return only admins and superusers
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
*/
public function scopeOnlySuperAdmins($query)
{
return $query->where('users.permissions', 'LIKE', '%"superuser":"1"%')
->orWhere('users.permissions', 'LIKE', '%"superuser":1%')
->orWhereHas(
'groups', function ($query) {
$query->where('permission_groups.permissions', 'LIKE', '%"superuser":"1"%')
->orWhere('permission_groups.permissions', 'LIKE', '%"superuser":1%');
}
);
}
/**
* Return only admins and superusers
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
*/
public function scopeOnlyAdminsAndSuperAdmins($query)
{
return $query->where('users.permissions', 'LIKE', '%"superuser":"1"%')
->orWhere('users.permissions', 'LIKE', '%"superuser":1%')
->orWhere('users.permissions', 'LIKE', '%"admin":1%')
->orWhere('users.permissions', 'LIKE', '%"admin":"1"%')
->orWhereHas(
'groups', function ($query) {
$query->where('permission_groups.permissions', 'LIKE', '%"superuser":"1"%')
->orWhere('permission_groups.permissions', 'LIKE', '%"superuser":1%')
->orWhere('permission_groups.permissions', 'LIKE', '%"admin":1%')
->orWhere('permission_groups.permissions', 'LIKE', '%"admin":"1"%');
}
);
}
/**
* Query builder scope to order on manager
@@ -1020,6 +964,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
/**
* Get the preferred locale for the user.
*
@@ -1058,6 +1003,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
public function scopeUserLocation($query, $location, $search)
{
return $query->where('location_id', '=', $location)
->where('users.first_name', 'LIKE', '%' . $search . '%')
->orWhere('users.email', 'LIKE', '%' . $search . '%')
@@ -1070,6 +1016,9 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
->orWhere('users.username', 'LIKE', '%' . $search . '%')
->orwhereRaw('CONCAT(users.first_name," ",users.last_name) LIKE \''.$search.'%\'');
}
/**

View File

@@ -29,7 +29,6 @@ class AcceptanceAssetAcceptedNotification extends Notification
$this->assigned_to = $params['assigned_to'];
$this->note = $params['note'];
$this->company_name = $params['company_name'];
$this->admin = $params['admin'] ?? null;
$this->settings = Setting::getSettings();
}
@@ -73,7 +72,6 @@ class AcceptanceAssetAcceptedNotification extends Notification
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'intro_text' => trans('mail.acceptance_asset_accepted'),
'admin' => $this->admin,
])
->subject(trans('mail.acceptance_asset_accepted'));

View File

@@ -20,11 +20,7 @@ class ComponentObserver
$logAction->item_type = Component::class;
$logAction->item_id = $component->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();
if($component->imported) {
$logAction->setActionSource('importer');
}
$logAction->logaction('update');
}
@@ -41,7 +37,6 @@ class ComponentObserver
$logAction->item_type = Component::class;
$logAction->item_id = $component->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();
if($component->imported) {
$logAction->setActionSource('importer');
@@ -61,7 +56,6 @@ class ComponentObserver
$logAction->item_type = Component::class;
$logAction->item_id = $component->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');
}

View File

@@ -1,74 +0,0 @@
<?php
namespace App\Observers;
use App\Models\Actionlog;
use App\Models\Maintenance;
use App\Models\Asset;
class MaintenanceObserver
{
/**
* Listen to the User created event.
*
* @param Maintenance $maintenance
* @return void
*/
public function updated(Maintenance $maintenance)
{
$logAction = new Actionlog();
$logAction->item_type = Maintenance::class;
$logAction->item_id = $maintenance->id;
$logAction->target_type = Asset::class;
$logAction->target_id = $maintenance->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();
if($maintenance->imported) {
$logAction->setActionSource('importer');
}
$logAction->logaction('update');
}
/**
* Listen to the Component created event when
* a new component is created.
*
* @param Maintenance $maintenance
* @return void
*/
public function created(Maintenance $maintenance)
{
$logAction = new Actionlog();
$logAction->item_type = Maintenance::class;
$logAction->item_id = $maintenance->id;
$logAction->target_type = Asset::class;
$logAction->target_id = $maintenance->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();
if($maintenance->imported) {
$logAction->setActionSource('importer');
}
$logAction->logaction('create');
}
/**
* Listen to the Component deleting event.
*
* @param Maintenance $maintenance
* @return void
*/
public function deleting(Maintenance $maintenance)
{
$logAction = new Actionlog();
$logAction->item_type = Maintenance::class;
$logAction->item_id = $maintenance->id;
$logAction->target_type = Asset::class;
$logAction->target_id = $maintenance->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');
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Policies;
use App\Models\User;
use App\Models\Asset;
class AssetPolicy extends CheckoutablePermissionsPolicy
{

View File

@@ -85,7 +85,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can view the model.
* Determine whether the user can view the accessory.
*
* @param \App\Models\User $user
* @return mixed
@@ -101,7 +101,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can create model.
* Determine whether the user can create accessories.
*
* @param \App\Models\User $user
* @return mixed
@@ -112,7 +112,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can update the model.
* Determine whether the user can update the accessory.
*
* @param \App\Models\User $user
* @return mixed
@@ -124,7 +124,7 @@ abstract class SnipePermissionsPolicy
/**
* Determine whether the user can update the model.
* Determine whether the user can update the accessory.
*
* @param \App\Models\User $user
* @return mixed
@@ -135,7 +135,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can delete the model.
* Determine whether the user can delete the accessory.
*
* @param \App\Models\User $user
* @return mixed
@@ -151,7 +151,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can manage the model.
* Determine whether the user can manage the accessory.
*
* @param \App\Models\User $user
* @return mixed

View File

@@ -5,7 +5,7 @@ namespace App\Presenters;
/**
* Class AssetModelPresenter
*/
class MaintenancesPresenter extends Presenter
class AssetMaintenancesPresenter extends Presenter
{
/**
* Json Column Layout for bootstrap table
@@ -22,7 +22,7 @@ class MaintenancesPresenter extends Presenter
'title' => trans('general.id'),
'visible' => false,
], [
'field' => 'name',
'field' => 'title',
'searchable' => true,
'sortable' => true,
'switchable' => true,
@@ -30,15 +30,6 @@ class MaintenancesPresenter extends Presenter
'visible' => true,
'formatter' => 'maintenancesLinkFormatter',
],
[
'field' => 'image',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'imageFormatter',
],
[
'field' => 'company',
'searchable' => true,
@@ -51,7 +42,7 @@ class MaintenancesPresenter extends Presenter
'field' => 'asset_name',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/table.asset_name'),
'title' => trans('admin/asset_maintenances/table.asset_name'),
'formatter' => 'assetNameLinkFormatter',
], [
'field' => 'asset_tag',
@@ -98,35 +89,35 @@ class MaintenancesPresenter extends Presenter
'field' => 'asset_maintenance_type',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/form.asset_maintenance_type'),
'title' => trans('admin/asset_maintenances/form.asset_maintenance_type'),
], [
'field' => 'start_date',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/form.start_date'),
'title' => trans('admin/asset_maintenances/form.start_date'),
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'completion_date',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/form.completion_date'),
'title' => trans('admin/asset_maintenances/form.completion_date'),
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'notes',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/form.notes'),
'title' => trans('admin/asset_maintenances/form.notes'),
], [
'field' => 'is_warranty',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/table.is_warranty'),
'title' => trans('admin/asset_maintenances/table.is_warranty'),
'formatter' => 'trueFalseFormatter'
], [
'field' => 'cost',
'searchable' => true,
'sortable' => true,
'title' => trans('admin/maintenances/form.cost'),
'title' => trans('admin/asset_maintenances/form.cost'),
'class' => 'text-right',
], [
'field' => 'created_by',

View File

@@ -328,7 +328,7 @@ class AssetPresenter extends Presenter
// name can break the listings page. - snipe
foreach ($fields as $field) {
$layout[] = [
'field' => $field->db_column,
'field' => 'custom_fields.'.$field->db_column,
'searchable' => true,
'sortable' => true,
'switchable' => true,

View File

@@ -106,7 +106,7 @@ class HistoryPresenter extends Presenter
'switchable' => true,
'title' => trans('general.file_name'),
'visible' => true,
'formatter' => 'fileNameFormatter',
'formatter' => 'fileUploadNameFormatter',
],
[
'field' => 'file_download',
@@ -115,7 +115,7 @@ class HistoryPresenter extends Presenter
'switchable' => true,
'title' => trans('general.download'),
'visible' => true,
'formatter' => 'fileDownloadButtonsFormatter',
'formatter' => 'fileUploadFormatter',
],
[
'field' => 'note',

View File

@@ -53,7 +53,6 @@ class LicensePresenter extends Presenter
'searchable' => true,
'sortable' => true,
'title' => trans('admin/licenses/form.to_email'),
'formatter' => 'emailFormatter',
], [
'field' => 'license_name',
'searchable' => true,

View File

@@ -67,9 +67,7 @@ class LocationPresenter extends Presenter
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/message.current_location'),
'titleTooltip' => trans('admin/locations/message.current_location'),
'visible' => true,
'class' => 'css-house-laptop',
], [
'field' => 'rtd_assets_count',
'searchable' => false,

View File

@@ -18,7 +18,7 @@ class UploadedFilesPresenter extends Presenter
$layout = [
[
'field' => 'id',
'searchable' => true,
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.id'),
@@ -30,7 +30,6 @@ class UploadedFilesPresenter extends Presenter
'sortable' => false,
'switchable' => false,
'title' => trans('general.type'),
'visible' => true,
'formatter' => 'iconFormatter',
],
[
@@ -39,17 +38,16 @@ class UploadedFilesPresenter extends Presenter
'sortable' => false,
'switchable' => true,
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'filePreviewFormatter',
'formatter' => 'inlineImageFormatter',
],
[
'field' => 'filename',
'searchable' => true,
'sortable' => true,
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.file_name'),
'visible' => true,
'formatter' => 'fileNameFormatter',
'formatter' => 'fileUploadNameFormatter',
],
[
'field' => 'download',
@@ -58,7 +56,7 @@ class UploadedFilesPresenter extends Presenter
'switchable' => true,
'title' => trans('general.download'),
'visible' => true,
'formatter' => 'fileDownloadButtonsFormatter',
'formatter' => 'downloadOrOpenInNewWindowFormatter',
],
[
'field' => 'note',
@@ -70,10 +68,10 @@ class UploadedFilesPresenter extends Presenter
],
[
'field' => 'created_by',
'searchable' => true,
'searchable' => false,
'sortable' => true,
'title' => trans('general.created_by'),
'visible' => true,
'visible' => false,
'formatter' => 'usersLinkObjFormatter',
],
[
@@ -82,7 +80,7 @@ class UploadedFilesPresenter extends Presenter
'sortable' => true,
'switchable' => true,
'title' => trans('general.created_at'),
'visible' => true,
'visible' => false,
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'available_actions',
@@ -90,7 +88,6 @@ class UploadedFilesPresenter extends Presenter
'sortable' => false,
'switchable' => false,
'title' => trans('table.actions'),
'visible' => true,
'formatter' => 'deleteUploadFormatter',
],
];

View File

@@ -124,15 +124,6 @@ class UserPresenter extends Presenter
'visible' => true,
'formatter' => 'phoneFormatter',
],
[
'field' => 'mobile',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/users/table.mobile'),
'visible' => false,
'formatter' => 'mobileFormatter',
],
[
'field' => 'website',
'searchable' => true,
@@ -189,7 +180,7 @@ class UserPresenter extends Presenter
'switchable' => false,
'title' => trans('admin/users/table.username'),
'visible' => true,
'formatter' => 'usernameRoleLinkFormatter',
'formatter' => 'usersLinkFormatter',
],
[
'field' => 'employee_num',

View File

@@ -7,7 +7,6 @@ use App\Models\Asset;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Maintenance;
use App\Models\User;
use App\Models\Setting;
use App\Models\SnipeSCIMConfig;
@@ -18,7 +17,6 @@ use App\Observers\ComponentObserver;
use App\Observers\ConsumableObserver;
use App\Observers\LicenseObserver;
use App\Observers\SettingObserver;
use App\Observers\MaintenanceObserver;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
@@ -69,7 +67,6 @@ class AppServiceProvider extends ServiceProvider
Schema::defaultStringLength(191);
Asset::observe(AssetObserver::class);
Maintenance::observe(MaintenanceObserver::class);
User::observe(UserObserver::class);
Accessory::observe(AccessoryObserver::class);
Component::observe(ComponentObserver::class);
@@ -85,12 +82,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
}
// Only load rollbar if there is a rollbar key and the app is in production
if (($this->app->environment('production')) && (config('logging.channels.rollbar.access_token'))) {
$this->app->register(\Rollbar\Laravel\RollbarServiceProvider::class);

View File

@@ -2,7 +2,7 @@
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\AssetMaintenance;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\Company;
@@ -413,14 +413,14 @@ class BreadcrumbsServiceProvider extends ServiceProvider
->push(trans('general.create'), route('maintenances.create'))
);
Breadcrumbs::for('maintenances.show', fn (Trail $trail, Maintenance $maintenance) =>
Breadcrumbs::for('maintenances.show', fn (Trail $trail, AssetMaintenance $maintenance) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push($maintenance->name, route('maintenances.show', $maintenance))
->push($maintenance->title, route('maintenances.show', $maintenance))
);
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, Maintenance $maintenance) =>
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, AssetMaintenance $maintenance) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push(trans('general.breadcrumb_button_actions.edit_item', ['name' => $maintenance->name]), route('maintenances.edit', $maintenance))
->push(trans('general.breadcrumb_button_actions.edit_item', ['name' => $maintenance->title]), route('maintenances.edit', $maintenance))
);
@@ -539,26 +539,6 @@ class BreadcrumbsServiceProvider extends ServiceProvider
->push(trans('general.users'), route('users.index'))
->push(trans('general.deleted_users'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->admins=='true')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.show_admins'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->superadmins=='true')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.show_superadmins'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->activated=='0')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.login_disabled'), route('users.index'))
);
} elseif ((request()->is('users*')) && (request()->activated=='1')) {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))
->push(trans('general.login_enabled'), route('users.index'))
);
} else {
Breadcrumbs::for('users.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.users'), route('users.index'))

View File

@@ -31,7 +31,7 @@ class SettingsServiceProvider extends ServiceProvider
// Make sure the limit is actually set, is an integer and does not exceed system limits
app()->singleton('api_limit_value', function () {
\App::singleton('api_limit_value', function () {
$limit = config('app.max_results');
$int_limit = intval(request('limit'));
@@ -43,7 +43,7 @@ class SettingsServiceProvider extends ServiceProvider
});
// Make sure the offset is actually set and is an integer
app()->singleton('api_offset_value', function () {
\App::singleton('api_offset_value', function () {
$offset = intval(request('offset'));
return $offset;
});
@@ -57,121 +57,117 @@ class SettingsServiceProvider extends ServiceProvider
// Model paths and URLs
app()->singleton('eula_pdf_path', function () {
\App::singleton('eula_pdf_path', function () {
return 'eula_pdf_path/';
});
app()->singleton('assets_upload_path', function () {
\App::singleton('assets_upload_path', function () {
return 'assets/';
});
app()->singleton('maintenances_path', function () {
return 'maintenances/';
});
app()->singleton('audits_upload_path', function () {
\App::singleton('audits_upload_path', function () {
return 'audits/';
});
app()->singleton('accessories_upload_path', function () {
\App::singleton('accessories_upload_path', function () {
return 'public/uploads/accessories/';
});
app()->singleton('models_upload_path', function () {
\App::singleton('models_upload_path', function () {
return 'models/';
});
app()->singleton('models_upload_url', function () {
\App::singleton('models_upload_url', function () {
return 'models/';
});
// Categories
app()->singleton('categories_upload_path', function () {
\App::singleton('categories_upload_path', function () {
return 'categories/';
});
app()->singleton('categories_upload_url', function () {
\App::singleton('categories_upload_url', function () {
return 'categories/';
});
// Locations
app()->singleton('locations_upload_path', function () {
\App::singleton('locations_upload_path', function () {
return 'locations/';
});
app()->singleton('locations_upload_url', function () {
\App::singleton('locations_upload_url', function () {
return 'locations/';
});
// Users
app()->singleton('users_upload_path', function () {
\App::singleton('users_upload_path', function () {
return 'avatars/';
});
app()->singleton('users_upload_url', function () {
\App::singleton('users_upload_url', function () {
return 'users/';
});
// Manufacturers
app()->singleton('manufacturers_upload_path', function () {
\App::singleton('manufacturers_upload_path', function () {
return 'manufacturers/';
});
app()->singleton('manufacturers_upload_url', function () {
\App::singleton('manufacturers_upload_url', function () {
return 'manufacturers/';
});
// Suppliers
app()->singleton('suppliers_upload_path', function () {
\App::singleton('suppliers_upload_path', function () {
return 'suppliers/';
});
app()->singleton('suppliers_upload_url', function () {
\App::singleton('suppliers_upload_url', function () {
return 'suppliers/';
});
// Departments
app()->singleton('departments_upload_path', function () {
\App::singleton('departments_upload_path', function () {
return 'departments/';
});
app()->singleton('departments_upload_url', function () {
\App::singleton('departments_upload_url', function () {
return 'departments/';
});
// Company paths and URLs
app()->singleton('companies_upload_path', function () {
\App::singleton('companies_upload_path', function () {
return 'companies/';
});
app()->singleton('companies_upload_url', function () {
\App::singleton('companies_upload_url', function () {
return 'companies/';
});
// Accessories paths and URLs
app()->singleton('accessories_upload_path', function () {
\App::singleton('accessories_upload_path', function () {
return 'accessories/';
});
app()->singleton('accessories_upload_url', function () {
\App::singleton('accessories_upload_url', function () {
return 'accessories/';
});
// Consumables paths and URLs
app()->singleton('consumables_upload_path', function () {
\App::singleton('consumables_upload_path', function () {
return 'consumables/';
});
app()->singleton('consumables_upload_url', function () {
\App::singleton('consumables_upload_url', function () {
return 'consumables/';
});
// Components paths and URLs
app()->singleton('components_upload_path', function () {
\App::singleton('components_upload_path', function () {
return 'components/';
});
app()->singleton('components_upload_url', function () {
\App::singleton('components_upload_url', function () {
return 'components/';
});

View File

@@ -1,66 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Telescope::night();
$this->hideSensitiveRequestDetails();
$isLocal = $this->app->environment('local');
Telescope::filter(function (IncomingEntry $entry) use ($isLocal) {
return $isLocal ||
$entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}
/**
* Prevent sensitive request details from being logged by Telescope.
*/
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters(['_token']);
Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}
/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in NON-LOCAL environments.
*/
protected function gate(): void
{
Gate::define('viewTelescope', function ($user) {
if ($user->isSuperUser()) {
return true;
}
return false;
});
}
}

View File

@@ -5,11 +5,9 @@ namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class AlphaEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
@@ -20,7 +18,7 @@ class AlphaEncrypted implements ValidationRule
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateAlpha($attributeName, $decrypted, 'ascii') && !is_null($decrypted)) {
if (!ctype_alpha($decrypted) && !is_null($decrypted)) {
$fail(trans('validation.alpha', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {

View File

@@ -1,33 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class BooleanEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateBoolean($attributeName, $decrypted) && !is_null($decrypted)) {
$fail(trans('validation.ipv6', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class DateEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateDate($attributeName, $decrypted) && !is_null($decrypted)) {
$fail(trans('validation.date', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class EmailEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateEmail($attribute, $decrypted, []) && !is_null($decrypted)) {
$fail(trans('validation.email', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class IPEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateIp($attributeName, $decrypted) && !is_null($decrypted)) {
$fail(trans('validation.ip', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class IPv4Encrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateIpv4($attributeName, $decrypted) && !is_null($decrypted)) {
$fail(trans('validation.ipv4', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class IPv6Encrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateIpv6($attributeName, $decrypted) && !is_null($decrypted)) {
$fail(trans('validation.ipv6', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,33 +0,0 @@
<?php
namespace App\Rules;
use App\Models\CustomField;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class MacEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateRegex($attributeName, $decrypted, ['/^[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}$/']) && !is_null($decrypted)) {
$fail(trans('validation.mac_address', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -3,16 +3,12 @@
namespace App\Rules;
use Closure;
use Egulias\EmailValidator\Validation\RFCValidation;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class NumericEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
@@ -20,10 +16,11 @@ class NumericEncrypted implements ValidationRule
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateNumeric($attributeName, $decrypted) && !is_null($decrypted)) {
if (!is_numeric($decrypted) && !is_null($decrypted)) {
$fail(trans('validation.numeric', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {

View File

@@ -1,36 +0,0 @@
<?php
namespace App\Rules;
use App\Models\CustomField;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class RegexEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$field = CustomField::where('db_column', $attribute)->first();
$regex = $field->format;
$regex = str_replace('regex:', '', $regex);
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateRegex($attributeName, $decrypted, [$regex]) && !is_null($decrypted)) {
$fail(trans('validation.regex', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e->getMessage());
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class UrlEncrypted implements ValidationRule
{
use ValidatesAttributes;
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
try {
$attributeName = trim(preg_replace('/_+|snipeit|\d+/', ' ', $attribute));
$decrypted = Crypt::decrypt($value);
if (!$this->validateUrl($attributeName, $decrypted, []) && !is_null($decrypted)) {
$fail(trans('validation.url', ['attribute' => $attributeName]));
}
} catch (\Exception $e) {
report($e);
$fail(trans('general.something_went_wrong'));
}
}
}

View File

@@ -37,7 +37,7 @@
"doctrine/dbal": "^3.1",
"doctrine/instantiator": "^1.3",
"eduardokum/laravel-mail-auto-embed": "^2.0",
"enshrined/svg-sanitize": "^0.22.0",
"enshrined/svg-sanitize": "^0.16.0",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.24",
"guzzlehttp/guzzle": "^7.0.1",
@@ -84,7 +84,6 @@
},
"require-dev": {
"larastan/larastan": "^2.9",
"laravel/telescope": "^5.11",
"mockery/mockery": "^1.4",
"nunomaduro/phpinsights": "^2.11",
"php-mock/php-mock-phpunit": "^2.10",
@@ -96,8 +95,7 @@
"extra": {
"laravel": {
"dont-discover": [
"rollbar/rollbar-laravel",
"laravel/telescope"
"rollbar/rollbar-laravel"
]
}
},

149
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "41f2c8e1296de21aaf82d4b1bfbc1800",
"content-hash": "75fb4f46ea0a488c2dd45d73eb2a9b9d",
"packages": [
{
"name": "alek13/slack",
@@ -1678,25 +1678,26 @@
},
{
"name": "enshrined/svg-sanitize",
"version": "0.22.0",
"version": "0.16.0",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500"
"reference": "239e257605e2141265b429e40987b2ee51bba4b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500",
"reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/239e257605e2141265b429e40987b2ee51bba4b4",
"reference": "239e257605e2141265b429e40987b2ee51bba4b4",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.1 || ^8.0"
"ezyang/htmlpurifier": "^4.16",
"php": "^5.6 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5 || ^8.5"
"phpunit/phpunit": "^5.7 || ^6.5 || ^8.5"
},
"type": "library",
"autoload": {
@@ -1717,9 +1718,9 @@
"description": "An SVG sanitizer for PHP",
"support": {
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0"
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.16.0"
},
"time": "2025-08-12T10:13:48+00:00"
"time": "2023-03-20T10:51:12+00:00"
},
{
"name": "erusev/parsedown",
@@ -1771,6 +1772,67 @@
},
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.18.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "cb56001e54359df7ae76dc522d08845dc741621b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b",
"reference": "cb56001e54359df7ae76dc522d08845dc741621b",
"shasum": ""
},
"require": {
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"cerdic/css-tidy": "^1.7 || ^2.0",
"simpletest/simpletest": "dev-master"
},
"suggest": {
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
"ext-bcmath": "Used for unit conversion and imagecrash protection",
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
"ext-tidy": "Used for pretty-printing HTML"
},
"type": "library",
"autoload": {
"files": [
"library/HTMLPurifier.composer.php"
],
"psr-0": {
"HTMLPurifier": "library/"
},
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0"
},
"time": "2024-11-01T03:51:45+00:00"
},
{
"name": "fakerphp/faker",
"version": "v1.24.1",
@@ -12883,75 +12945,6 @@
],
"time": "2025-06-10T22:06:33+00:00"
},
{
"name": "laravel/telescope",
"version": "v5.11.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/telescope.git",
"reference": "62e1a21db3db3e7440e9ca02ffa3efce14d6b85e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/telescope/zipball/62e1a21db3db3e7440e9ca02ffa3efce14d6b85e",
"reference": "62e1a21db3db3e7440e9ca02ffa3efce14d6b85e",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^8.37|^9.0|^10.0|^11.0|^12.0",
"php": "^8.0",
"symfony/console": "^5.3|^6.0|^7.0",
"symfony/var-dumper": "^5.0|^6.0|^7.0"
},
"require-dev": {
"ext-gd": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"laravel/octane": "^1.4|^2.0|dev-develop",
"orchestra/testbench": "^6.40|^7.37|^8.17|^9.0|^10.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.0|^10.5|^11.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Telescope\\TelescopeServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Telescope\\": "src/",
"Laravel\\Telescope\\Database\\Factories\\": "database/factories/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
},
{
"name": "Mohamed Said",
"email": "mohamed@laravel.com"
}
],
"description": "An elegant debug assistant for the Laravel framework.",
"keywords": [
"debugging",
"laravel",
"monitoring"
],
"support": {
"issues": "https://github.com/laravel/telescope/issues",
"source": "https://github.com/laravel/telescope/tree/v5.11.2"
},
"time": "2025-08-16T00:52:51+00:00"
},
{
"name": "league/container",
"version": "5.1.0",

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