Compare commits

..

1 Commits

Author SHA1 Message Date
snipe
c125eb8e6e Moved faker out of dev so we can use the seeders
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 17:17:19 +01:00
2169 changed files with 315950 additions and 53265 deletions

View File

@@ -4162,33 +4162,6 @@
"contributions": [
"code"
]
},
{
"login": "Tinyblargon",
"name": "Okean",
"avatar_url": "https://avatars.githubusercontent.com/u/76069640?v=4",
"profile": "https://github.com/Tinyblargon",
"contributions": [
"code"
]
},
{
"login": "amedranogil",
"name": "Alejandro Medrano",
"avatar_url": "https://avatars.githubusercontent.com/u/6515064?v=4",
"profile": "https://www.lst.tfo.upm.es/alejandro-medrano/",
"contributions": [
"code"
]
},
{
"login": "lukaskraic",
"name": "Lukas Kraic",
"avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
"profile": "https://github.com/lukaskraic",
"contributions": [
"code"
]
}
]
}

View File

@@ -28,7 +28,6 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=db
DB_SOCKET=null
DB_PORT='3306'
DB_DATABASE=snipeit
DB_USERNAME=snipeit
@@ -169,7 +168,6 @@ AWS_DEFAULT_REGION=null
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@@ -24,7 +24,6 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_SOCKET=null
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
@@ -175,7 +174,6 @@ LOGIN_AUTOCOMPLETE=false
RESET_PASSWORD_LINK_EXPIRES=15
PASSWORD_CONFIRM_TIMEOUT=10800
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@@ -36,7 +36,7 @@ jobs:
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@v4.4.7
uses: codacy/codacy-analysis-cli-action@v4.4.5
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations

View File

@@ -67,7 +67,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -1,68 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Console\Command;
class CleanIncorrectCheckoutAcceptances extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-checkout-acceptances';
/**
* The console command description.
*
* @var string
*/
protected $description = "Delete checkout acceptances for checkouts to non-users";
/**
* Execute the console command.
*/
public function handle()
{
$deletions = 0;
$skips = 0;
// This walks *every* checkoutacceptance. That's gnarly. But necessary
$this->withProgressBar(CheckoutAcceptance::all(), function ($checkoutAcceptance) use (&$deletions, &$skips) {
$item = $checkoutAcceptance->checkoutable;
$checkout_to_id = $checkoutAcceptance->assigned_to_id;
if(is_null($item)) {
$this->info("'Checkoutable' Item is null, going to next record");
return; //'false' allegedly breaks execution entirely, so 'true' maybe doesn't? hrm. just straight return maybe?
}
if(get_class($item) == LicenseSeat::class) {
$item = $item->license;
}
foreach($item->assetlog()->where('action_type','checkout')->get() as $assetlog) {
if ($assetlog->target_id == $checkout_to_id && $assetlog->target_type != User::class) {
//We have a checkout-to an ID for a non-User, which matches to an ID in the checkout_acceptances table
//now, let's compare the _times_ - are they close?
//I'm picking `created_at` over `action_date` because I'm more interested in when the actionlogs
//were _created_, not when they were alleged to have happened - those created_at times need to be within 'X' seconds of
//each other (currently 5)
if ($assetlog->created_at->diffInSeconds($checkoutAcceptance->created_at, true) <= 5) { //we're allowing for five _ish_ seconds of slop
$deletions++;
$checkoutAcceptance->forceDelete(); // HARD delete this record; it should have never been
return;
} else {
//$this->info("The two records are too far apart");
}
} else {
//$this->info("No match! checkout to id: " . $checkout_to_id." target_id: ".$assetlog->target_id." target_type: ".$assetlog->target_type);
}
}
$skips++;
});
$this->error("Final deletion count: $deletions, and skip count: $skips");
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class FixUpAssignedTypeWithoutAssignedTo extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-type-fixup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
/**
* Execute the console command.
*/
public function handle()
{
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
$this->info("Assets with an assigned_type but no assigned_to are fixed");
}
}

View File

@@ -182,7 +182,7 @@ class LdapSync extends Command
// Inject location information fields
for ($i = 0; $i < $results['count']; $i++) {
$results[$i]['ldap_location_override'] = false;
$results[$i]['location_id'] = null;
$results[$i]['location_id'] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.

View File

@@ -4,7 +4,7 @@ namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\CustomField;
use Illuminate\Support\Facades\Schema;
use Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Command;
@@ -66,8 +66,8 @@ class PaveIt extends Command
foreach ($custom_fields as $custom_field) {
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
if (Schema::hasColumn('assets', $custom_field->db_column)) {
Schema::table('assets', function ($table) use ($custom_field) {
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
\Schema::table('assets', function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
}
@@ -84,8 +84,8 @@ class PaveIt extends Command
}
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
DB::statement('delete from oauth_clients WHERE id > 2');
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
\DB::statement('delete from oauth_clients WHERE id > 2');
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
}
}

View File

@@ -329,9 +329,9 @@ class RestoreFromBackup extends Command
}
}
}
$good_extensions = config('filesystems.allowed_upload_extensions_array');
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico', 'avif'
];
foreach (array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {

View File

@@ -99,11 +99,8 @@ class SendAcceptanceReminder extends Command
foreach ($no_email_list as $user) {
$rows[] = [$user['id'], $user['name']];
}
if (!empty($rows)) {
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
}
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
return 0;
}

View File

@@ -28,7 +28,7 @@ class CheckoutableCheckedIn
$this->checkedOutTo = $checkedOutTo;
$this->checkedInBy = $checkedInBy;
$this->note = $note;
$this->action_date = $action_date ?? date('Y-m-d H:i:s');
$this->action_date = $action_date ?? date('Y-m-d');
$this->originalValues = $originalValues;
}
}

View File

@@ -11,7 +11,6 @@ use Illuminate\Support\Facades\Log;
use Throwable;
use JsonException;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
class Handler extends ExceptionHandler
{
@@ -108,42 +107,26 @@ class Handler extends ExceptionHandler
$statusCode = $e->getStatusCode();
// API throttle requests are handled in the RouteServiceProvider configureRateLimiting() method, so we don't need to handle them here
switch ($e->getStatusCode()) {
case '404':
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
case '429':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
case '405':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
}
}
// This handles API validation exceptions that happen at the Form Request level, so they
// never even get to the controller where we normally nicely format JSON responses
if ($e instanceof ValidationException) {
$response = $this->invalidJson($request, $e);
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
}
}
// This is traaaaash but it handles models that are not found while using route model binding :(
// The only alternative is to set that at *each* route, which is crazypants
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$ids = method_exists($e, 'getIds') ? $e->getIds() : [];
if (in_array('bulkedit', $ids, true)) {
$error_array = session()->get('bulk_asset_errors');
return redirect()
->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
// 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';
@@ -160,10 +143,6 @@ class Handler extends ExceptionHandler
$route = 'maintenances.index';
} elseif ($route === 'licenseseats.index') {
$route = 'licenses.index';
} elseif ($route === 'customfields.index') {
$route = 'fields.index';
} elseif ($route === 'customfieldsets.index') {
$route = 'fields.index';
}
return redirect()
@@ -222,7 +201,6 @@ class Handler extends ExceptionHandler
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});

View File

@@ -13,7 +13,6 @@ use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\License;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
use Carbon\Carbon;
@@ -877,48 +876,6 @@ class Helper
return false;
}
/**
* Check if the file is a video, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [B. Wetherington] [<bwetherington@grokability.com>]
* @since [v8.1.18]
*/
public static function checkUploadIsVideo($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'video/mp4') || ($filetype == 'video/quicktime') || ($filetype == 'video/mpeg') || ($filetype == 'video/ogg') || ($filetype == 'video/webm') || ($filetype == 'video/x-msvide')) {
return $filetype;
}
return false;
}
/**
* Check if the file is audio, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*/
public static function checkUploadIsAudio($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'audio/mpeg') || ($filetype == 'audio/ogg')) {
return $filetype;
}
return false;
}
/**
* Walks through the permissions in the permissions config file and determines if
* permissions are granted based on a $selected_arr array.
@@ -1213,15 +1170,6 @@ class Helper
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
// video
'mov' => 'fa-solid fa-video',
'mp4' => 'fa-solid fa-video',
// audio
'ogg' => 'fa-solid fa-file-audio',
'mp3' => 'fa-solid fa-file-audio',
'wav' => 'fa-solid fa-file-audio',
];
if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
@@ -1365,24 +1313,25 @@ class Helper
switch ($item) {
case 'asset':
return 'fas fa-barcode';
break;
case 'accessory':
return 'fas fa-keyboard';
break;
case 'component':
return 'fas fa-hdd';
break;
case 'consumable':
return 'fas fa-tint';
break;
case 'license':
return 'far fa-save';
break;
case 'location':
return 'fas fa-map-marker-alt';
break;
case 'user':
return 'fas fa-user';
case 'supplier':
return 'fa-solid fa-store';
case 'manufacturer':
return 'fa-solid fa-building';
case 'category':
return 'fa-solid fa-table-columns';
break;
}
}
@@ -1532,64 +1481,68 @@ class Helper
}
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
static public function getRedirectOption($request, $id, $table, $item_id = null)
{
$redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
$checkout_to_type = Session::get('checkout_to_type') ?? null;
$redirect_option = Session::get('redirect_option');
$checkout_to_type = Session::get('checkout_to_type');
$checkedInFrom = Session::get('checkedInFrom');
$other_redirect = Session::get('other_redirect');
$backUrl = Session::pull('back_url', route('home'));
// 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);
}
// return to index
if ($redirect_option == 'index') {
return match ($table) {
'Assets' => redirect()->route('hardware.index'),
'Users' => redirect()->route('users.index'),
'Licenses' => redirect()->route('licenses.index'),
'Accessories' => redirect()->route('accessories.index'),
'Components' => redirect()->route('components.index'),
'Consumables' => redirect()->route('consumables.index'),
};
switch ($table) {
case "Assets":
return route('hardware.index');
case "Users":
return route('users.index');
case "Licenses":
return route('licenses.index');
case "Accessories":
return route('accessories.index');
case "Components":
return route('components.index');
case "Consumables":
return route('consumables.index');
}
}
// return to thing being assigned
if ($redirect_option == 'item') {
return match ($table) {
'Assets' => redirect()->route('hardware.show', $id ?? $item_id),
'Users' => redirect()->route('users.show', $id ?? $item_id),
'Licenses' => redirect()->route('licenses.show', $id ?? $item_id),
'Accessories' => redirect()->route('accessories.show', $id ?? $item_id),
'Components' => redirect()->route('components.show', $id ?? $item_id),
'Consumables' => redirect()->route('consumables.show', $id ?? $item_id),
};
switch ($table) {
case "Assets":
return route('hardware.show', $id ?? $item_id);
case "Users":
return route('users.show', $id ?? $item_id);
case "Licenses":
return route('licenses.show', $id ?? $item_id);
case "Accessories":
return route('accessories.show', $id ?? $item_id);
case "Components":
return route('components.show', $id ?? $item_id);
case "Consumables":
return route('consumables.show', $id ?? $item_id);
}
}
// return to assignment target
if ($redirect_option == 'target') {
return match ($checkout_to_type) {
'user' => redirect()->route('users.show', $request->assigned_user ?? $checkedInFrom),
'location' => redirect()->route('locations.show', $request->assigned_location ?? $checkedInFrom),
'asset' => redirect()->route('hardware.show', $request->assigned_asset ?? $checkedInFrom),
};
switch ($checkout_to_type) {
case 'user':
return route('users.show', $request->assigned_user ?? $checkedInFrom);
case 'location':
return route('locations.show', $request->assigned_location ?? $checkedInFrom);
case 'asset':
return route('hardware.show', $request->assigned_asset ?? $checkedInFrom);
}
}
// return to somewhere else
if ($redirect_option == 'other_redirect') {
return match ($other_redirect) {
'audit' => redirect()->route('assets.audit.due'),
'model' => redirect()->route('models.show', $request->model_id),
};
switch ($other_redirect) {
case 'audit':
return route('assets.audit.due');
}
}

View File

@@ -151,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

@@ -16,75 +16,38 @@ class StorageHelper
$disk = config('filesystems.default');
}
switch (config("filesystems.disks.$disk.driver")) {
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
default:
return Storage::disk($disk)->download($filename);
default:
return Storage::disk($disk)->download($filename);
}
}
public static function getMediaType($file_with_path) {
// The file exists and is allowed to be displayed inline
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';
default:
return $extension; // Default for unknown types
}
}
return null;
}
/**
* This determines the file types that should be allowed inline and checks their fileinfo extension
* to determine that they are safe to display inline.
*
* @author <A. Gianotto> [<snipe@snipe.net]>
* @since v7.0.14
* @param $file_with_path
* @since v7.0.14
* @param $file_with_path
* @return bool
*/
public static function allowSafeInline($file_with_path)
{
public static function allowSafeInline($file_with_path) {
$allowed_inline = [
'avif',
'gif',
'gif',
'jpg',
'mov',
'mp3',
'mp4',
'ogg',
'pdf',
'png',
'svg',
'wav',
'webm',
'jpg',
'gif',
'svg',
'avif',
'webp',
'png',
];
@@ -96,24 +59,10 @@ class StorageHelper
}
public static function getFiletype($file_with_path)
{
// The file exists and is allowed to be displayed inline
if (Storage::exists($file_with_path)) {
return pathinfo($file_with_path, PATHINFO_EXTENSION);
}
return null;
}
/**
* Decide whether to show the file inline or download it.
*/
public static function showOrDownloadFile($file, $filename)
{
public static function showOrDownloadFile($file, $filename) {
$headers = [];

View File

@@ -77,25 +77,13 @@ 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);
}
$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
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -125,12 +113,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);
}
@@ -180,8 +167,7 @@ class AccessoriesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
}
} else {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));

View File

@@ -78,8 +78,7 @@ class AccessoryCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
}
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));

View File

@@ -97,7 +97,7 @@ class AccessoryCheckoutController extends Controller
// Redirect to the new accessory page
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
->with('success', trans('admin/accessories/message.checkout.success'));
}
}

View File

@@ -7,7 +7,6 @@ use App\Events\CheckoutDeclined;
use App\Events\ItemAccepted;
use App\Events\ItemDeclined;
use App\Http\Controllers\Controller;
use App\Mail\CheckoutAcceptanceResponseMail;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
@@ -21,12 +20,9 @@ use App\Models\License;
use App\Models\Component;
use App\Models\Consumable;
use App\Notifications\AcceptanceAssetAcceptedNotification;
use App\Notifications\AcceptanceAssetAcceptedToUserNotification;
use App\Notifications\AcceptanceAssetDeclinedNotification;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Http\Controllers\SettingsController;
@@ -152,8 +148,6 @@ class AcceptanceController extends Controller
}
}
$assigned_user = User::find($acceptance->assigned_to_id);
// this is horrible
switch($acceptance->checkoutable_type){
case 'App\Models\Asset':
@@ -163,30 +157,35 @@ class AcceptanceController extends Controller
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
}
$display_model = $asset_model->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Accessory':
$pdf_view_route ='account.accept.accept-accessory-eula';
$accessory = Accessory::find($item->id);
$display_model = $accessory->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\LicenseSeat':
$pdf_view_route ='account.accept.accept-license-eula';
$license = License::find($item->license_id);
$display_model = $license->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Component':
$pdf_view_route ='account.accept.accept-component-eula';
$component = Component::find($item->id);
$display_model = $component->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Consumable':
$pdf_view_route ='account.accept.accept-consumable-eula';
$consumable = Consumable::find($item->id);
$display_model = $consumable->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
}
// if ($acceptance->checkoutable_type == 'App\Models\Asset') {
@@ -227,7 +226,7 @@ class AcceptanceController extends Controller
'note' => $request->input('note'),
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'),
'assigned_to' => $assigned_user->present()->fullName,
'assigned_to' => $assigned_to,
'company_name' => $branding_settings->site_name,
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => $path_logo,
@@ -241,19 +240,6 @@ class AcceptanceController extends Controller
}
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
// Send the PDF to the signing user
if (($request->input('send_copy') == '1') && ($assigned_user->email !='')) {
// Add the attachment for the signing user into the $data array
$data['file'] = $pdf_filename;
try {
$assigned_user->notify(new AcceptanceAssetAcceptedToUserNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}
}
try {
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
} catch (\Exception $e) {
@@ -351,21 +337,6 @@ class AcceptanceController extends Controller
$return_msg = trans('admin/users/message.declined');
}
if ($acceptance->alert_on_response_id) {
try {
$recipient = User::find($acceptance->alert_on_response_id);
if ($recipient) {
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
$acceptance,
$recipient,
$request->input('asset_acceptance') === 'accepted',
));
}
} catch (Exception $e) {
Log::warning($e);
}
}
return redirect()->to('account/accept')->with('success', $return_msg);

View File

@@ -0,0 +1,200 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use App\Http\Transformers\UploadedFilesTransformer;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Illuminate\Http\Request;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $asset);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assets')) {
Storage::makeDirectory('private_uploads/assets', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
$asset->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function list(Asset $asset, Request $request) : JsonResponse | array
{
$this->authorize('view', $asset);
$allowed_columns =
[
'id',
'filename',
'eol',
'notes',
'created_at',
'updated_at',
];
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')->where('item_type', '=', Asset::class)->where('item_id', '=', $asset->id);
if ($request->filled('search')) {
$files = $files->TextSearch($request->input('search'));
}
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $files->count()) ? $files->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$files = $files->orderBy($sort, $order);
$files = $files->skip($offset)->take($limit)->get();
return (new UploadedFilesTransformer())->transformFiles($files, $files->count());
}
/**
* Check for permissions and display the file.
*
* @param int $assetId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function show(Asset $asset, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assets/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetId
* @param int $fileId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function destroy(Asset $asset, $fileId = null) : JsonResponse
{
$rel_path = 'private_uploads/assets';
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
}

View File

@@ -75,7 +75,6 @@ class AssetMaintenancesController extends Controller
'serial',
'created_by',
'supplier',
'location',
'is_warranty',
'status_label',
];
@@ -99,9 +98,6 @@ class AssetMaintenancesController extends Controller
case 'serial':
$maintenances = $maintenances->OrderByAssetSerial($order);
break;
case 'location':
$maintenances = $maintenances->OrderLocationName($order);
break;
case 'status_label':
$maintenances = $maintenances->OrderStatusName($order);
break;

View File

@@ -0,0 +1,184 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\AssetModel;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\AssetModelsTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetModelFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetModelId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $assetModel);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assetmodels')) {
Storage::makeDirectory('private_uploads/assetmodels', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
$assetModel->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetmodel
* @since [v7.0.12]
* @author [r-xyz]
*/
public function list($assetmodel_id) : JsonResponse | array
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetmodel_id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
$this->authorize('view', $assetmodel);
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
}
/**
* Check for permissions and display the file.
*
* @param int $assetModelId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v7.0.12]
* @author [r-xyz]
*/
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('view', $assetModel);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assetmodels/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetModelId
* @param int $fileId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
$rel_path = 'private_uploads/assetmodels';
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('update', $assetModel);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
}

View File

@@ -85,12 +85,6 @@ class AssetModelsController extends Controller
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
}
if ($request->input('requestable') == 'true') {
$assetmodels = $assetmodels->where('models.requestable', '=', '1');
} elseif ($request->input('requestable') == 'false') {
$assetmodels = $assetmodels->where('models.requestable', '=', '0');
}
if ($request->filled('notes')) {
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
}
@@ -154,7 +148,7 @@ class AssetModelsController extends Controller
$assetmodel = $request->handleImages($assetmodel);
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
@@ -207,7 +201,7 @@ class AssetModelsController extends Controller
$assetmodel = AssetModel::findOrFail($id);
$assetmodel->fill($request->all());
$assetmodel = $request->handleImages($assetmodel);
/**
* Allow custom_fieldset_id to override and populate fieldset_id.
* This is stupid, but required for legacy API support.
@@ -222,7 +216,7 @@ class AssetModelsController extends Controller
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));

View File

@@ -114,7 +114,6 @@ class AssetsController extends Controller
'byod',
'asset_eol_date',
'requestable',
'jobtitle',
];
$filter = [];
@@ -396,9 +395,6 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
case 'jobtitle':
$assets->OrderByJobTitle($order);
break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;
@@ -1150,7 +1146,7 @@ class AssetsController extends Controller
// Validate the rest of the data before we turn off the event dispatcher
if ($asset->isInvalid()) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag' => $asset->asset_tag], $asset->getErrors()));
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
}

View File

@@ -43,10 +43,7 @@ class CompaniesController extends Controller
$companies = Company::withCount(['assets as assets_count' => function ($query) {
$query->AssetsForShow();
}])
->with('adminuser')
->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
}])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
if ($request->filled('search')) {
$companies->TextSearch($request->input('search'));
@@ -122,7 +119,6 @@ class CompaniesController extends Controller
{
$this->authorize('view', Company::class);
$company = Company::findOrFail($id);
$this->authorize('view', $company);
return (new CompaniesTransformer)->transformCompany($company);
}
@@ -140,7 +136,6 @@ class CompaniesController extends Controller
{
$this->authorize('update', Company::class);
$company = Company::findOrFail($id);
$this->authorize('update', $company);
$company->fill($request->all());
$company = $request->handleImages($company);
@@ -193,7 +188,6 @@ class CompaniesController extends Controller
'companies.image',
]);
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}

View File

@@ -234,15 +234,6 @@ class ImportController extends Controller
case 'location':
$redirectTo = 'locations.index';
break;
case 'supplier':
$redirectTo = 'suppliers.index';
break;
case 'manufacturer':
$redirectTo = 'manufacturers.index';
break;
case 'category':
$redirectTo = 'categories.index';
break;
}
if ($errors) { //Failure

View File

@@ -29,15 +29,6 @@ class LicenseSeatsController extends Controller
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
if ($request->input('status') == 'available') {
$seats->whereNull('license_seats.assigned_to');
}
if ($request->input('status') == 'assigned') {
$seats->ByAssigned();
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort') == 'department') {

View File

@@ -87,8 +87,7 @@ class LocationsController extends Controller
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count')
->with('adminuser');
->withCount('users as users_count');
// Only scope locations if the setting is enabled
if (Setting::getSettings()->scope_locations_fmcs) {
@@ -219,7 +218,6 @@ class LocationsController extends Controller
'locations.updated_at',
'locations.image',
'locations.currency',
'locations.company_id',
'locations.notes',
])
->withCount('assignedAssets as assigned_assets_count')

View File

@@ -4,19 +4,15 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\ProfileTransformer;
use App\Models\CheckoutRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Laravel\Passport\TokenRepository;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Support\Facades\Gate;
use App\Models\CustomField;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ProfileController extends Controller
{
@@ -171,22 +167,6 @@ class ProfileController extends Controller
}
/**
* Display the EULAs accepted by the user.
*
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
* @return \Illuminate\Http\JsonResponse
*@since [v8.1.16]
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
*/
public function eulas(ProfileTransformer $transformer)
{
// Only return this user's EULAs
$eulas = auth()->user()->eulas;
return response()->json(
$transformer->transformFiles($eulas, $eulas->count())
);
}
}

View File

@@ -24,15 +24,10 @@ class SuppliersController extends Controller
public function index(Request $request): array
{
$this->authorize('view', Supplier::class);
$allowed_columns = [
'id',
$allowed_columns = ['
id',
'name',
'address',
'address2',
'city',
'state',
'country',
'zip',
'phone',
'contact',
'fax',
@@ -44,24 +39,21 @@ class SuppliersController extends Controller
'components_count',
'consumables_count',
'url',
'notes',
];
$suppliers = Supplier::select(
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'created_by', 'updated_at', 'deleted_at', 'image', 'notes', 'url', 'zip'])
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes', 'url'])
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count')
->withCount('consumables as consumables_count')
->with('adminuser');
->withCount('consumables as consumables_count');
if ($request->filled('search')) {
$suppliers->TextSearch($request->input('search'));
$suppliers = $suppliers->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$suppliers->where('name', '=', $request->input('name'));
}
@@ -108,15 +100,7 @@ class SuppliersController extends Controller
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
switch ($request->input('sort')) {
case 'created_by':
$suppliers->OrderByCreatedByName($order);
break;
default:
$suppliers->orderBy($sort, $order);
break;
}
$suppliers->orderBy($sort, $order);
$total = $suppliers->count();
$suppliers = $suppliers->skip($offset)->take($limit)->get();

View File

@@ -1,268 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\UploadedFilesTransformer;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Location;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class UploadedFilesController extends Controller
{
static $map_object_type = [
'accessories' => Accessory::class,
'assets' => Asset::class,
'components' => Component::class,
'consumables' => Consumable::class,
'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
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to list files for
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function index(Request $request, $object_type, $id) : JsonResponse | array
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Columns allowed for sorting
$allowed_columns =
[
'id',
'filename',
'action_type',
'action_date',
'note',
'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'));
$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';
// 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
if ($request->filled('search')) {
$uploads->where(
function ($query) use ($request) {
$query->where('filename', 'LIKE', '%' . $request->input('search') . '%')
->orWhere('note', 'LIKE', '%' . $request->input('search') . '%');
}
);
}
$total = $uploads->count();
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
return (new UploadedFilesTransformer())->transformFiles($uploads, $total);
}
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to store so we can check permisisons
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, $object_type, $id) : JsonResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// If the file storage directory doesn't exist, create it
if (! Storage::exists(self::$map_storage_path[$object_type])) {
Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
}
if ($request->hasFile('file')) {
// Loop over the attached files and add them to the object
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
$files[] = $file_name;
$object->logUpload($file_name, $request->get('notes'));
}
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
->where('item_type', '=', self::$map_object_type[$object_type])
->where('item_id', '=', $id)->whereIn('filename', $files)
->get();
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
}
// No files were submitted
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles')));
}
/**
* Check for permissions and display the file.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to delete from so we can check permisisons
* @param $file_id the ID of the file to delete from the action_logs table
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Check that the file being requested exists for the object
if (! $log = Actionlog::whereNotNull('filename')->where('item_type', self::$map_object_type[$object_type])->where('item_id', $object->id)->find($file_id)
) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 200);
}
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200));
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
}
return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
}
/**
* Delete the associated file
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to delete from so we can check permisisons
* @param $file_id the ID of the file to delete from the action_logs table
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function destroy($object_type, $id, $file_id) : JsonResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('update', self::$map_object_type[$object_type]);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Check for the file
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
->where('item_id', $object->id)->first();
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
}
// Delete the record of the file
if ($log->delete()) {
return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200);
}
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
}
}

View File

@@ -6,7 +6,6 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\SaveUserRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\ActionlogsTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\ConsumablesTransformer;
use App\Http\Transformers\LicensesTransformer;
@@ -23,7 +22,6 @@ use App\Notifications\CurrentInventory;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;
@@ -82,12 +80,7 @@ class UsersController extends Controller
'users.autoassign_licenses',
'users.website',
])->with('manager')
->with('groups')
->with('userloc')
->with('company')
->with('department')
->with('createdBy')
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
->withCount([
'assets as assets_count' => function(Builder $query) {
$query->withoutTrashed();
@@ -108,14 +101,6 @@ 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'));
}
@@ -221,11 +206,11 @@ class UsersController extends Controller
}
if ($request->filled('manages_users_count')) {
$users->has('managesUsers', '=', $request->input('manages_users_count'));
$users->has('manages_users_count', '=', $request->input('manages_users_count'));
}
if ($request->filled('manages_locations_count')) {
$users->has('managedLocations', '=', $request->input('manages_locations_count'));
$users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
}
if ($request->filled('autoassign_licenses')) {
@@ -489,25 +474,8 @@ class UsersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
// check for permissions related fields and pull them out if the current user cannot edit them
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
if ($request->filled('username')) {
$user->username = $request->input('username');
}
if ($request->filled('email')) {
$user->email = $request->input('email');
}
if ($request->filled('activated')) {
$user->activated = $request->input('activated');
}
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
// We need to use has() instead of filled()
@@ -708,6 +676,7 @@ class UsersController extends Controller
$this->authorize('view', License::class);
if ($user = User::where('id', $id)->withTrashed()->first()) {
$this->authorize('update', $user);
$licenses = $user->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
@@ -767,25 +736,6 @@ class UsersController extends Controller
return (new UsersTransformer)->transformUser($request->user());
}
/**
* Display the EULAs accepted by the user.
*
* @param \App\Models\User $user
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
* @return \Illuminate\Http\JsonResponse
*@since [v8.1.16]
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
*/
public function eulas(User $user, ActionlogsTransformer $transformer)
{
$this->authorize('view', User::class);
$eulas = $user->eulas;
return response()->json(
$transformer->transformActionlogs($eulas, $eulas->count())
);
}
/**
* Restore a soft-deleted user.
*

View File

@@ -19,6 +19,19 @@ use \Illuminate\Http\RedirectResponse;
*/
class AssetMaintenancesController extends Controller
{
/**
* Checks for permissions for this action.
*
* @todo This should be replaced with middleware and/or policies
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
private static function getInsufficientPermissionsRedirect(): RedirectResponse
{
return redirect()->route('maintenances.index')
->with('error', trans('general.insufficient_permissions'));
}
/**
* Returns a view that invokes the ajax tables which actually contains
@@ -54,10 +67,16 @@ class AssetMaintenancesController extends Controller
// We have to set this so that the correct property is set in the select2 ajax dropdown
$asset->asset_id = $asset->id;
}
// Prepare Asset Maintenance Type List
$assetMaintenanceType = [
'' => 'Select an asset maintenance type',
] + AssetMaintenance::getImprovementOptions();
// Mark the selected asset, if it came in
return view('asset_maintenances/edit')
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('asset', $asset)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', new AssetMaintenance);
}
@@ -72,45 +91,43 @@ class AssetMaintenancesController extends Controller
public function store(Request $request) : RedirectResponse
{
$this->authorize('update', Asset::class);
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
$asset = Asset::find($request->input('asset_id'));
$assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
// Loop through the selected assets
foreach ($assets as $asset) {
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
// Save the asset maintenance data
$assetMaintenance->asset_id = $asset->id;
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->created_by = auth()->id();
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if (!$assetMaintenance->save()) {
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
if ((! Company::isCurrentUserHasAccess($asset)) && ($asset != null)) {
return static::getInsufficientPermissionsRedirect();
}
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.create.success'));
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->created_by = auth()->id();
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
/**
@@ -118,19 +135,26 @@ class AssetMaintenancesController extends Controller
*
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
*/
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Prepare Improvement Type List
$assetMaintenanceType = ['' => 'Select an improvement type'] + AssetMaintenance::getImprovementOptions();
return view('asset_maintenances/edit')
->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
->with('asset_ids', request()->input('asset_ids', []))
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('item', $maintenance);
->with('selectedAsset', null)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', $maintenance);
}
/**
@@ -146,21 +170,33 @@ class AssetMaintenancesController extends Controller
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
$maintenance->supplier_id = $request->input('supplier_id');
$maintenance->is_warranty = $request->input('is_warranty', 0);
$maintenance->is_warranty = $request->input('is_warranty');
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$asset = Asset::find(request('asset_id'));
if (! Company::isCurrentUserHasAccess($asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Save the asset maintenance data
$maintenance->asset_id = $request->input('asset_id');
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$maintenance->title = $request->input('title');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
// Todo - put this in a getter/setter?
if (($maintenance->completion_date == null))
{
if (($maintenance->completion_date == null)
) {
if (($maintenance->asset_maintenance_time !== 0)
|| (! is_null($maintenance->asset_maintenance_time))
) {
@@ -177,7 +213,10 @@ class AssetMaintenancesController extends Controller
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if ($maintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.edit.success'));
}
@@ -193,12 +232,21 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function destroy(AssetMaintenance $maintenance) : RedirectResponse
public function destroy($assetMaintenanceId) : RedirectResponse
{
$this->authorize('update', Asset::class);
$this->authorize('update', $maintenance->asset);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Delete the asset maintenance
$maintenance->delete();
$assetMaintenance->delete();
// Redirect to the asset_maintenance management page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.delete.success'));
@@ -214,6 +262,11 @@ class AssetMaintenancesController extends Controller
*/
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('view', Asset::class);
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
}
}

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

@@ -96,6 +96,7 @@ class AssetCheckinController extends Controller
});
$asset->expected_checkin = null;
$asset->last_checkin = now();
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
$asset->name = $request->get('name');
@@ -122,14 +123,11 @@ class AssetCheckinController extends Controller
$originalValues = $asset->getRawOriginal();
// Handle last checkin date
$checkin_at = date('Y-m-d H:i:s');
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
$originalValues['action_date'] = $checkin_at;
$checkin_at = $request->get('checkin_at');
}
$asset->last_checkin = $checkin_at;
$asset->licenseseats->each(function (LicenseSeat $seat) {
$seat->update(['assigned_to' => null]);
@@ -153,8 +151,7 @@ class AssetCheckinController extends Controller
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());

View File

@@ -123,7 +123,7 @@ class AssetCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return Helper::getRedirectOption($request, $asset->id, 'Assets')
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error

View File

@@ -149,7 +149,7 @@ class AssetsController extends Controller
$asset->byod = request('byod', 0);
if (! empty($settings->audit_interval)) {
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
$asset->next_audit_date = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
}
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
@@ -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);
}
@@ -196,31 +188,14 @@ class AssetsController extends Controller
// Validate the asset before saving
if ($asset->isValid() && $asset->save()) {
$target = null;
$location = null;
if ($userId = request('assigned_user')) {
$target = User::find($userId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
}
if (request('assigned_user')) {
$target = User::find(request('assigned_user'));
$location = $target->location_id;
} elseif ($assetId = request('assigned_asset')) {
$target = Asset::find($assetId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
}
} elseif (request('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
$location = $target->location_id;
} elseif ($locationId = request('assigned_location')) {
$target = Location::find($locationId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
}
} elseif (request('assigned_location')) {
$target = Location::find(request('assigned_location'));
$location = $target->id;
}
@@ -235,27 +210,24 @@ class AssetsController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => 'model' ]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($successes) {
if ($failures) {
//some succeeded, some failed
return Helper::getRedirectOption($request, $asset->id, 'Assets') //FIXME - not tested
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
} else {
if (count($successes) == 1) {
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
//and re-translated
return Helper::getRedirectOption($request, $asset->id, 'Assets')
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset), 'id', 'tag' => e($asset->asset_tag)]));
} else {
//multi-success
return Helper::getRedirectOption($request, $asset->id, 'Assets')
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
}
}
@@ -276,7 +248,6 @@ class AssetsController extends Controller
public function edit(Asset $asset) : View | RedirectResponse
{
$this->authorize($asset);
session()->put('back_url', url()->previous());
return view('hardware/edit')
->with('item', $asset)
->with('statuslabel_list', Helper::statusLabelList())
@@ -439,15 +410,11 @@ class AssetsController extends Controller
}
}
}
session()->put([
'redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => $request->get('redirect_option') === 'other_redirect' ? 'model' : null,
]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->save()) {
return Helper::getRedirectOption($request, $asset->id, 'Assets')
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -479,7 +446,7 @@ class AssetsController extends Controller
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues));
DB::table('assets')
->where('id', $asset->id)
->update(['assigned_to' => null, 'assigned_type' => null]);
->update(['assigned_to' => null]);
}
@@ -491,7 +458,6 @@ class AssetsController extends Controller
}
}
$asset->delete();
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
@@ -652,9 +618,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 = '';
@@ -664,7 +629,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);
}
@@ -926,7 +890,7 @@ class AssetsController extends Controller
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
}
$dt = Carbon::now()->addMonths( (int) $settings->audit_interval)->toDateString();
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
}
@@ -1014,7 +978,7 @@ class AssetsController extends Controller
}
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
return Helper::getRedirectOption($request, $asset->id, 'Assets')->with('success', trans('admin/hardware/message.audit.success'));
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.audit.success'));
}
return redirect()->back()->withInput()->withErrors($asset->getErrors());

View File

@@ -52,26 +52,11 @@ class BulkAssetsController extends Controller
}
$asset_ids = $request->input('ids');
if ($request->input('bulk_actions') === 'checkout') {
$status_check =$this->hasUndeployableStatus($asset_ids);
if($status_check && $status_check['status'] === true){
$asset_tags = implode(', ', array_column($status_check['tags'], 'asset_tag'));
$asset_ids = $status_check['asset_ids'];
session()->flash('warning', trans('admin/hardware/message.undeployable', ['asset_tags' => $asset_tags]));
}
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('hardware.bulkcheckout.show');
}
if ($request->input('bulk_actions') === 'maintenance') {
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('maintenances.create');
}
// Figure out where we need to send the user after the update is complete, and store that in the session
$bulk_back_url = request()->headers->get('referer');
session(['bulk_back_url' => $bulk_back_url]);
@@ -112,47 +97,11 @@ class BulkAssetsController extends Controller
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
$query = Asset::with('assignedTo', 'location', 'model')
$assets = Asset::with('assignedTo', 'location', 'model')
->whereIn('assets.id', $asset_ids)
->withTrashed();
switch ($sort_override) {
case 'model':
$query->OrderModels($order);
break;
case 'model_number':
$query->OrderModelNumber($order);
break;
case 'category':
$query->OrderCategory($order);
break;
case 'manufacturer':
$query->OrderManufacturer($order);
break;
case 'company':
$query->OrderCompany($order);
break;
case 'location':
$query->OrderLocation($order);
break;
case 'rtd_location':
$query->OrderRtdLocation($order);
break;
case 'status_label':
$query->OrderStatus($order);
break;
case 'supplier':
$query->OrderSupplier($order);
break;
case 'assigned_to':
$query->OrderAssigned($order);
break;
default:
$query->orderBy($column_sort, $order);
break;
}
$assets = $query->get();
$assets = $assets->get();
if ($assets->isEmpty()) {
Log::debug('No assets were found for the provided IDs', ['ids' => $asset_ids]);
@@ -161,7 +110,6 @@ class BulkAssetsController extends Controller
$models = $assets->unique('model_id');
$modelNames = [];
foreach($models as $model) {
$modelNames[] = $model->model->name;
}
@@ -197,6 +145,7 @@ class BulkAssetsController extends Controller
case 'edit':
$this->authorize('update', Asset::class);
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
@@ -205,7 +154,40 @@ class BulkAssetsController extends Controller
}
}
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
case 'company':
$assets->OrderCompany($order);
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
break;
case 'supplier':
$assets->OrderSupplier($order);
break;
case 'assigned_to':
$assets->OrderAssigned($order);
break;
default:
$assets->orderBy($column_sort, $order);
break;
}
return redirect()->back()->with('error', 'No action selected');
}
@@ -224,25 +206,13 @@ class BulkAssetsController extends Controller
$error_array = array();
// Get the back url from the session and then destroy the session
$bulk_back_url = route('hardware.index');
$bulk_back_url = $request->session()->pull('bulk_back_url', url()->previous());
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
// find custom field input attributes that start with 'null_'
$null_custom_fields_inputs = array_filter($request->all(), function ($key) {
// filter out all keys that start with 'null_'
return (strpos($key, 'null_') === 0);
}, ARRAY_FILTER_USE_KEY);;
// remove 'null' from the keys
$custom_fields_to_null = [];
foreach ($null_custom_fields_inputs as $key => $value) {
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
if ($request->session()->has('bulk_back_url')) {
$bulk_back_url = $request->session()->pull('bulk_back_url');
}
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
@@ -281,9 +251,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
|| ($request->filled('null_asset_eol_date'))
|| ($request->filled('null_notes'))
|| ($request->anyFilled($custom_field_columns))
|| ($request->anyFilled(array_keys($null_custom_fields_inputs)))
) {
// Let's loop through those assets and build an update array
@@ -306,14 +274,10 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date')
->conditionallyAddItem('asset_eol_date')
->conditionallyAddItem('notes');
->conditionallyAddItem('asset_eol_date');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
$this->conditionallyAddItem($key);
}
if (!($asset->eol_explicit)) {
if ($request->filled('model_id')) {
@@ -364,10 +328,6 @@ class BulkAssetsController extends Controller
}
}
if ($request->input('null_notes')=='1') {
$this->update_array['notes'] = null;
}
if ($request->filled('purchase_cost')) {
@@ -408,12 +368,10 @@ class BulkAssetsController extends Controller
// This could probably be added to a form request.
// If the asset isn't assigned, we don't care what the status is.
// Otherwise we need to make sure the status type is still a deployable one.
$unassigned = $asset->assigned_to == '';
$deployable = $updated_status->deployable == '1' && $asset->assetstatus?->deployable == '1';
$pending = $updated_status->pending === 1;
if ($unassigned || $deployable || $pending) {
if (
($asset->assigned_to == '')
|| ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
) {
$this->update_array['status_id'] = $updated_status->id;
}
@@ -465,7 +423,6 @@ class BulkAssetsController extends Controller
}
/**
*
* Start all the custom fields shenanigans
*/
@@ -473,15 +430,6 @@ class BulkAssetsController extends Controller
if ($asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
// null custom fields
if ($custom_fields_to_null) {
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
if ($field->db_column == $key) {
$this->update_array[$field->db_column] = null;
}
}
}
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
if (Gate::allows('admin')) {
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
@@ -540,13 +488,7 @@ class BulkAssetsController extends Controller
} // end asset foreach
if ($has_errors > 0) {
session()->put('bulkedit_ids', $request->input('ids'));
session()->put('bulk_asset_errors',$error_array);
return redirect()
->route('hardware.bulkedit')
->with('bulk_asset_errors', $error_array)
->withInput();
return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array);
}
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success'));
@@ -620,10 +562,7 @@ class BulkAssetsController extends Controller
public function showCheckout() : View
{
$this->authorize('checkout', Asset::class);
$do_not_change = ['' => trans('general.do_not_change')];
$status_label_list = $do_not_change + Helper::deployableStatusLabelList();
return view('hardware/bulk-checkout')->with('statusLabel_list', $status_label_list);
return view('hardware/bulk-checkout');
}
/**
@@ -655,13 +594,13 @@ class BulkAssetsController extends Controller
}
$checkout_at = date('Y-m-d H:i:s');
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
$checkout_at = $request->get('checkout_at');
$checkout_at = e($request->get('checkout_at'));
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = $request->get('expected_checkin');
$expected_checkin = e($request->get('expected_checkin'));
}
$errors = [];
@@ -669,11 +608,6 @@ class BulkAssetsController extends Controller
foreach ($assets as $asset) {
$this->authorize('checkout', $asset);
// See if there is a status label passed
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
}
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
//TODO - I think this logic is duplicated in the checkOut method?
@@ -717,54 +651,4 @@ class BulkAssetsController extends Controller
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
}
}
public function hasUndeployableStatus (array $asset_ids)
{
$undeployable = Asset::whereIn('id', $asset_ids)
->undeployable()
->get();
$undeployableTags = $undeployable->map(function ($asset) {
return [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
})->toArray();
$undeployableIds = array_column($undeployableTags, 'id');
$filtered_ids = array_diff($asset_ids, $undeployableIds);
if($undeployable->isNotEmpty()) {
return ['status' => true, 'tags' => $undeployableTags, 'asset_ids' => $filtered_ids];
}
return false;
}
public function bulkEditForm(): View|RedirectResponse
{
$this->authorize('update', Asset::class);
$asset_ids = session()->pull('bulkedit_ids', []);
if (empty($asset_ids)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.no_assets_selected'));
}
$assets = Asset::with('model')->withTrashed()->whereIn('id', $asset_ids)->get();
if ($assets->isEmpty()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.assets_do_not_exist_or_are_invalid'));
}
$models = $assets->unique('model_id');
$modelNames = [];
foreach ($models as $model) {
$modelNames[] = $model->model->name;
}
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
->with('models', $models->pluck(['model']))
->with('modelNames', $modelNames);
}
}

View File

@@ -68,7 +68,6 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
$category->created_by = auth()->id();
@@ -122,7 +121,6 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');

View File

@@ -123,13 +123,11 @@ final class CompaniesController extends Controller
*/
public function destroy($companyId) : RedirectResponse
{
if (is_null($company = Company::find($companyId))) {
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
}
$this->authorize('delete', $company);
if (! $company->isDeletable()) {
return redirect()->route('companies.index')

View File

@@ -100,8 +100,8 @@ class ComponentCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success',
trans('admin/components/message.checkin.success'));
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));

View File

@@ -120,7 +120,6 @@ class ComponentCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkout.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.checkout.success'));
}
}

View File

@@ -91,8 +91,7 @@ class ComponentsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
@@ -112,7 +111,6 @@ class ComponentsController extends Controller
{
$this->authorize('update', $component);
session()->put('back_url', url()->previous());
return view('components/edit')
->with('item', $component)
->with('category_type', 'component');
@@ -166,8 +164,7 @@ class ComponentsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());

View File

@@ -111,7 +111,6 @@ class ConsumableCheckoutController extends Controller
// Redirect to the new consumable page
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.checkout.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
}
}

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,28 +81,16 @@ 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);
}
$consumable = $request->handleImages($consumable);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -119,7 +107,6 @@ class ConsumablesController extends Controller
public function edit(Consumable $consumable) : View | RedirectResponse
{
$this->authorize($consumable);
session()->put('back_url', url()->previous());
return view('consumables/edit')
->with('item', $consumable)
->with('category_type', 'consumable');
@@ -173,8 +160,7 @@ class ConsumablesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -224,10 +210,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

@@ -86,7 +86,7 @@ class LicenseCheckinController extends Controller
}
if($licenseSeat->assigned_to != null){
$return_to = User::withTrashed()->find($licenseSeat->assigned_to);
$return_to = User::find($licenseSeat->assigned_to);
session()->put('checkedInFrom', $return_to->id);
} else {
$return_to = Asset::find($licenseSeat->asset_id);
@@ -98,17 +98,14 @@ class LicenseCheckinController extends Controller
$licenseSeat->notes = $request->input('notes');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($request->get('redirect_option') === 'target'){
session()->put(['checkout_to_type' => 'user']);
}
// Was the asset updated?
if ($licenseSeat->save()) {
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
}
// Redirect to the license page with error

View File

@@ -89,8 +89,7 @@ class LicenseCheckoutController extends Controller
if ($checkoutTarget) {
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.checkout.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
}

View File

@@ -105,8 +105,7 @@ class LicensesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($license->getErrors());
@@ -126,7 +125,7 @@ class LicensesController extends Controller
{
$this->authorize('update', $license);
session()->put('back_url', url()->previous());
$maintained_list = [
'' => 'Maintained',
'1' => 'Yes',
@@ -182,8 +181,7 @@ class LicensesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.update.success'));
}
// If we can't adjust the number of seats, the error is flashed to the session by the event handler in License.php
return redirect()->back()->withInput()->withErrors($license->getErrors());
@@ -304,16 +302,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

@@ -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

@@ -3,21 +3,15 @@
namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\ProfileTransformer;
use App\Models\Actionlog;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This controller handles all actions related to User Profiles for
* the Snipe-IT Asset Management application.
@@ -226,7 +220,7 @@ class ProfileController extends Controller
if (!$user = User::find(auth()->id())) {
return redirect()->back()
->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
}
if (empty($user->email)) {
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
@@ -240,28 +234,4 @@ class ProfileController extends Controller
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
}
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$logentry = Actionlog::where('filename', $filename)->first();
// Make sure the user has permission to view this file
if (auth()->id() != $logentry->target_id) {
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
}
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View File

@@ -184,7 +184,7 @@ class ReportsController extends Controller
$currency = e(Setting::getSettings()->default_currency);
}
$row[] = Helper::getFormattedDateObject($asset->purchase_date, 'date', false);
$row[] = $asset->purchase_date;
$row[] = $currency.Helper::formatCurrencyOutput($asset->purchase_cost);
$row[] = $currency.Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$row[] = $currency.Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));

View File

@@ -352,7 +352,6 @@ class SettingsController extends Controller
$setting->dash_chart_type = $request->input('dash_chart_type');
$setting->profile_edit = $request->input('profile_edit', 0);
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
$setting->manager_view_enabled = $request->input('manager_view_enabled', 0);
if ($request->input('per_page') != '') {
@@ -651,7 +650,6 @@ class SettingsController extends Controller
$setting->alert_email = $alert_email;
$setting->admin_cc_email = $admin_cc_email;
$setting->admin_cc_always = $request->validated('admin_cc_always');
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
$setting->alert_interval = $request->input('alert_interval');
$setting->alert_threshold = $request->input('alert_threshold');
@@ -924,7 +922,7 @@ class SettingsController extends Controller
* @since v5.0.0
*/
public function postSamlSettings(SettingsSamlRequest $request) : RedirectResponse
{
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}

View File

@@ -25,21 +25,31 @@ class UserFilesController extends Controller
public function store(UploadFileRequest $request, User $user)
{
$this->authorize('update', $user);
$files = $request->file('file');
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/users')) {
Storage::makeDirectory('private_uploads/users', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/users/','user-'.$user->id, $file);
$user->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
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);
return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
//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'));
}
}

View File

@@ -13,8 +13,15 @@ use App\Models\Company;
use App\Models\Group;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Storage;
use Redirect;
use Str;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Notifications\CurrentInventory;
@@ -123,7 +130,7 @@ class UsersController extends Controller
}
$user->permissions = json_encode($permissions_array);
// we have to invoke the form request here to handle image uploads
// we have to invoke the
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
session()->put(['redirect_option' => $request->get('redirect_option')]);
@@ -135,8 +142,19 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.create'));
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
// Send the credentials through email
$data = [];
$data['email'] = e($request->input('email'));
$data['username'] = e($request->input('username'));
$data['first_name'] = e($request->input('first_name'));
$data['last_name'] = e($request->input('last_name'));
$data['password'] = e($request->input('password'));
$user->notify(new WelcomeNotification($data));
}
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -168,7 +186,6 @@ class UsersController extends Controller
{
$this->authorize('update', User::class);
session()->put('back_url', url()->previous());
$user = User::with(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed()->find($user->id);
if ($user) {
@@ -229,14 +246,20 @@ class UsersController extends Controller
}
}
// Only save groups if the user is a superuser
if (auth()->user()->isSuperUser()) {
$user->groups()->sync($request->input('groups'));
}
// Update the user fields
$user->username = trim($request->input('username'));
$user->email = trim($request->input('email'));
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
$user->locale = $request->input('locale');
$user->employee_num = $request->input('employee_num');
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle', null);
$user->phone = $request->input('phone');
$user->location_id = $request->input('location_id', null);
@@ -248,6 +271,8 @@ class UsersController extends Controller
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
// if a user is editing themselves we should always keep activated true
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
$user->vip = $request->input('vip', 0);
@@ -256,49 +281,30 @@ class UsersController extends Controller
$user->end_date = $request->input('end_date', null);
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
// Set this here so that we can overwrite it later if the user is an admin or superadmin
$user->activated = $request->input('activated', auth()->user()->is($user) ? 1 : $user->activated);
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)
->update(['location_id' => $request->input('location_id', null)]);
// check for permissions related fields and only set them if the user has permission to edit them
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->username = trim($request->input('username'));
$user->email = trim($request->input('email'));
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
// Do we want to update the user password?
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
$permissions_array = $request->input('permission');
// Strip out the superuser permission if the user isn't a superadmin
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
$permissions_array['superuser'] = $orig_superuser;
}
$user->permissions = json_encode($permissions_array);
// Only save groups if the user is a superuser
if (auth()->user()->isSuperUser()) {
$user->groups()->sync($request->input('groups'));
}
// Do we want to update the user password?
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)
->update(['location_id' => $user->location_id]);
$permissions_array = $request->input('permission');
// Strip out the superuser permission if the user isn't a superadmin
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
$permissions_array['superuser'] = $orig_superuser;
}
$user->permissions = json_encode($permissions_array);
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
@@ -306,7 +312,7 @@ class UsersController extends Controller
if ($user->save()) {
// Redirect to the user page
return Helper::getRedirectOption($request, $user->id, 'Users')
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
->with('success', trans('admin/users/message.success.update'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -432,7 +438,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);
@@ -447,8 +453,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');
@@ -464,7 +468,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);
}
@@ -506,8 +510,6 @@ class UsersController extends Controller
trans('admin/companies/table.title'),
trans('admin/users/table.title'),
trans('general.employee_number'),
trans('admin/users/table.first_name'),
trans('admin/users/table.last_name'),
trans('admin/users/table.name'),
trans('admin/users/table.username'),
trans('admin/users/table.email'),
@@ -553,8 +555,6 @@ class UsersController extends Controller
($user->company) ? $user->company->name : '',
$user->jobtitle,
$user->employee_num,
$user->first_name,
$user->last_name,
$user->present()->fullName(),
$user->username,
$user->email,

View File

@@ -27,126 +27,50 @@ use Exception;
class ViewAssetsController extends Controller
{
/**
* Extract custom fields that should be displayed in user view.
*
* @param User $user
* @return array
*/
private function extractCustomFields(User $user): array
{
$fieldArray = [];
foreach ($user->assets as $asset) {
if ($asset->model && $asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
if ($field->display_in_user_view == '1') {
$fieldArray[$field->db_column] = $field->name;
}
}
}
}
return array_unique($fieldArray);
}
/**
* Get list of users viewable by the current user.
*
* @param User $authUser
* @return \Illuminate\Support\Collection
*/
private function getViewableUsers(User $authUser): \Illuminate\Support\Collection
{
// SuperAdmin sees all users
if ($authUser->isSuperUser()) {
return User::select('id', 'first_name', 'last_name', 'username')
->where('activated', 1)
->orderBy('last_name')
->orderBy('first_name')
->get();
}
// Regular manager sees only their subordinates + self
$managedUsers = $authUser->getAllSubordinates();
// If user has subordinates, show them with self at beginning
if ($managedUsers->count() > 0) {
return collect([$authUser])->merge($managedUsers)
->sortBy('last_name')
->sortBy('first_name');
}
// User has no subordinates, only sees themselves
return collect([$authUser]);
}
/**
* Get the selected user ID from request or default to current user.
*
* @param Request $request
* @param \Illuminate\Support\Collection $subordinates
* @param int $defaultUserId
* @return int
*/
private function getSelectedUserId(Request $request, \Illuminate\Support\Collection $subordinates, int $defaultUserId): int
{
// If no subordinates or no user_id in request, return default
if ($subordinates->count() <= 1 || !$request->filled('user_id')) {
return $defaultUserId;
}
$requestedUserId = (int) $request->input('user_id');
// Validate if the requested user is allowed
if ($subordinates->contains('id', $requestedUserId)) {
return $requestedUserId;
}
// If invalid ID or not authorized, return default
return $defaultUserId;
}
/**
* Show user's assigned assets with optional manager view functionality.
* Redirect to the profile page.
*
*/
public function getIndex(Request $request) : View | RedirectResponse
public function getIndex() : View | RedirectResponse
{
$authUser = auth()->user();
$settings = Setting::getSettings();
$subordinates = collect();
$selectedUserId = $authUser->id;
// Process manager view if enabled
if ($settings->manager_view_enabled) {
$subordinates = $this->getViewableUsers($authUser);
$selectedUserId = $this->getSelectedUserId($request, $subordinates, $authUser->id);
}
// Load the data for the user to be viewed (either auth user or selected subordinate)
$userToView = User::with([
$user = User::with(
'assets',
'assets.model',
'assets.model.fieldset.fields',
'consumables',
'accessories',
'licenses'
])->find($selectedUserId);
'licenses',
)->find(auth()->id());
$field_array = array();
// Loop through all the custom fields that are applied to any model the user has assigned
foreach ($user->assets as $asset) {
// Make sure the model has a custom fieldset before trying to loop through the associated fields
if ($asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
// check and make sure they're allowed to see the value of the custom field
if ($field->display_in_user_view == '1') {
$field_array[$field->db_column] = $field->name;
}
}
}
// If the user to view couldn't be found (shouldn't happen with proper logic), redirect with error
if (!$userToView) {
return redirect()->route('view-assets')->with('error', trans('admin/users/message.user_not_found'));
}
// Process custom fields for the user being viewed
$fieldArray = $this->extractCustomFields($userToView);
// Since some models may re-use the same fieldsets/fields, let's make the array unique so we don't repeat columns
array_unique($field_array);
// Pass the necessary data to the view
return view('account/view-assets', [
'user' => $userToView, // Use 'user' for compatibility with the existing view
'field_array' => $fieldArray,
'settings' => $settings,
'subordinates' => $subordinates,
'selectedUserId' => $selectedUserId
]);
if (isset($user->id)) {
return view('account/view-assets', compact('user', 'field_array' ))
->with('settings', Setting::getSettings());
}
// Redirect to the user management page
return redirect()->route('users.index')
->with('error', trans('admin/users/message.user_not_found', $user->id));
}
/**

View File

@@ -73,7 +73,6 @@ class Kernel extends HttpKernel
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'api-throttle' => \App\Http\Middleware\SetAPIResponseHeaders::class,
'health' => null,
];
}

View File

@@ -1,82 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Symfony\Component\HttpFoundation\Response;
class SetAPIResponseHeaders extends ThrottleRequests
{
/**
* Add the rate limit headers to the response.
*
* This extends the original ThrottleRequests middleware to add the 'X-RateLimit-Reset' and 'Retry-After' headers, even
* if the rate limit is not exceeded.
* @param $maxAttempts
* @param $remainingAttempts
* @param $retryAfter
* @param Response|null $response
* @return array|int[]
*/
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null, ?Response $response = null)
{
if ($response &&
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
$headers = [];
$headers['Retry-After'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
return $headers;
}
$headers = [
'X-RateLimit-Limit' => $maxAttempts,
'X-RateLimit-Remaining' => $remainingAttempts,
];
if (! is_null($retryAfter)) {
$headers['Retry-After'] = $retryAfter;
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
}
return $headers;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
protected function handleRequest($request, Closure $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
}
$this->limiter->hit($limit->key, $limit->decaySeconds);
}
$response = $next($request);
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts),
$this->getTimeUntilNextRetry($limit->key) // this is the only line we changed
);
}
return $response;
}
}

View File

@@ -41,7 +41,6 @@ class SettingsSamlRequest extends FormRequest
public function withValidator($validator)
{
$validator->after(function ($validator) {
$setting = Setting::getSettings();
if ($this->input('saml_enabled') == '1') {
$idpMetadata = $this->input('saml_idp_metadata');
if (! empty($idpMetadata)) {
@@ -57,7 +56,7 @@ class SettingsSamlRequest extends FormRequest
}
}
$was_custom_x509cert = strpos($setting->saml_custom_settings, 'sp_x509cert') !== false;
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert = '';
$custom_privateKey = '';
@@ -127,14 +126,10 @@ class SettingsSamlRequest extends FormRequest
}
if (! (empty($x509cert) && empty($privateKey))) {
// $this->merge([
// 'saml_sp_x509cert' => $x509cert,
// 'saml_sp_privatekey' => $privateKey,
// ]);
$setting->saml_sp_x509cert = $x509cert;
$setting->saml_sp_privatekey = $privateKey;
$setting->save();
$this->merge([
'saml_sp_x509cert' => $x509cert,
'saml_sp_privatekey' => $privateKey,
]);
}
} else {
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
@@ -150,21 +145,15 @@ class SettingsSamlRequest extends FormRequest
}
if (! empty($x509certNew)) {
// $this->merge([
// 'saml_sp_x509certNew' => $x509certNew,
// ]);
$setting->saml_sp_x509certNew = $x509certNew;
$setting->save();
$this->merge([
'saml_sp_x509certNew' => $x509certNew,
]);
}
} else {
// $this->merge([
// 'saml_sp_x509certNew' => '',
// ]);
$setting->saml_sp_x509certNew = '';
$setting->save();
$this->merge([
'saml_sp_x509certNew' => '',
]);
}
});
}
}

View File

@@ -37,8 +37,8 @@ class StoreLabelSettings extends FormRequest
return [
'labels_per_page' => 'numeric',
'labels_width' => 'numeric|min:0.1',
'labels_height' => 'numeric|min:0.1',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_pmargin_left' => 'numeric|nullable',
'labels_pmargin_right' => 'numeric|nullable',
'labels_pmargin_top' => 'numeric|nullable',

View File

@@ -5,7 +5,6 @@ namespace App\Http\Requests;
use App\Models\Accessory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class StoreNotificationSettings extends FormRequest
{
@@ -27,9 +26,6 @@ class StoreNotificationSettings extends FormRequest
return [
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email_array|nullable',
'admin_cc_always' => [
Rule::in('0', '1'),
],
'alert_threshold' => 'numeric|nullable',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable',

View File

@@ -6,7 +6,6 @@ use App\Http\Traits\ConvertsBase64ToFiles;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \App\Helpers\Helper;
class UploadFileRequest extends Request
{
@@ -28,76 +27,44 @@ class UploadFileRequest extends Request
*/
public function rules()
{
$max_file_size = Helper::file_upload_max_size();
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
return [
'file.*' => 'required|mimes:'.config('filesystems.allowed_upload_extensions_for_validator').'|max:'.$max_file_size,
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
];
}
/**
* Sanitizes (if needed) and Saves a file to the appropriate location
* Returns the 'short' (storage-relative) filename
*
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
* a way to merge them or extend one into the other?
*/
public function handleFile(string $dirname, string $name_prefix, $file): string
{
$extension = $file->getClientOriginalExtension();
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
// Check for SVG and sanitize it
if ($file->getMimeType() === 'image/svg+xml') {
$uploaded_file = $this->handleSVG($file);
Log::debug('This is an SVG');
Log::debug($file_name);
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put($dirname.$file_name, $cleanSVG);
} catch (\Exception $e) {
Log::debug('Upload no workie :( ');
Log::debug($e);
}
} else {
$uploaded_file = file_get_contents($file);
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
}
try {
Storage::put($dirname.$file_name, $uploaded_file);
} catch (\Exception $e) {
Log::debug($e);
}
return $file_name;
}
public function handleSVG($file)
{
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
return $sanitizer->sanitize($dirtySVG);
}
/**
* Get the validation error messages that apply to the request, but
* replace the attribute name with the name of the file that was attempted and failed
* to make it clearer to the user which file is the bad one.
*
* @return array
*/
public function attributes(): array
{
$attributes = [];
if (($this->file) && (is_array($this->file))) {
for ($i = 0; $i < count($this->file); $i++) {
try {
if ($this->file[$i]) {
$attributes['file.'.$i] = $this->file[$i]->getClientOriginalName();
}
} catch (\Exception $e) {
$attributes['file.'.$i] = 'Invalid file';
}
}
}
return $attributes;
}
}
}

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
{
@@ -115,8 +113,8 @@ class ActionlogsTransformer
// Display the changes if the user is an admin or superadmin
if (Gate::allows('admin')) {
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old, ['allowed_classes' => false]) : '';
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new, ['allowed_classes' => false]) : '';
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old): '';
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): '';
}
}
@@ -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) ? [
@@ -184,8 +198,8 @@ class ActionlogsTransformer
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'remote_ip' => e($actionlog->remote_ip) ?? null,
'user_agent' => e($actionlog->user_agent) ?? null,
'remote_ip' => ($actionlog->remote_ip) ?? null,
'user_agent' => ($actionlog->user_agent) ?? null,
'action_source' => ($actionlog->action_source) ?? null,
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
];

View File

@@ -59,10 +59,7 @@ class AssetMaintenancesTransformer
'name'=> e($assetmaintenance->asset->defaultLoc->name),
] : null,
'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedownInline($assetmaintenance->notes) : null,
'supplier' => ($assetmaintenance->supplier) ? [
'id' => $assetmaintenance->supplier->id,
'name'=> e($assetmaintenance->supplier->name)
] : null,
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),

View File

@@ -101,7 +101,7 @@ class AssetsTransformer
'checkout_counter' => (int) $asset->checkout_counter,
'requests_counter' => (int) $asset->requests_counter,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()),
];
@@ -203,7 +203,6 @@ class AssetsTransformer
'last_name'=> ($asset->assigned->last_name) ? e($asset->assigned->last_name) : null,
'email'=> ($asset->assigned->email) ? e($asset->assigned->email) : null,
'employee_number' => ($asset->assigned->employee_num) ? e($asset->assigned->employee_num) : null,
'jobtitle' => $asset->assigned->jobtitle ? e($asset->assigned->jobtitle) : null,
'type' => 'user',
] : null;
}

View File

@@ -4,10 +4,6 @@ namespace App\Http\Transformers;
class DatatablesTransformer
{
/**
* Transform data for bootstrap tables and API responses for lists of things
**/
public function transformDatatables($objects, $total = null)
{
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
@@ -15,15 +11,4 @@ class DatatablesTransformer
return $objects_array;
}
/**
* Transform data for returning the status of items within a bulk action
**/
public function transformBulkResponseWithStatusAndObjects($objects, $total)
{
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
$objects_array['rows'] = $objects;
return $objects_array;
}
}
}

View File

@@ -26,11 +26,11 @@ class DepreciationsTransformer
$array = [
'id' => (int) $depreciation->id,
'name' => e($depreciation->name),
'months' => trans_choice('general.months_plural', $depreciation->months),
'months' => $depreciation->months.' '.trans('general.months'),
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
'assets_count' => ($depreciation->assets_count > 0) ? (int) $depreciation->assets_count : 0,
'models_count' => ($depreciation->models_count > 0) ? (int) $depreciation->models_count : 0,
'licenses_count' => ($depreciation->licenses_count > 0) ? (int) $depreciation->licenses_count : 0,
'assets_count' => $depreciation->assets_count,
'models_count' => $depreciation->models_count,
'licenses_count' => $depreciation->licenses_count,
'created_by' => ($depreciation->adminuser) ? [
'id' => (int) $depreciation->adminuser->id,
'name'=> e($depreciation->adminuser->present()->fullName()),

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

@@ -57,10 +57,6 @@ class LocationsTransformer
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
'notes' => Helper::parseEscapedMarkedownInline($location->notes),
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
'created_by' => $location->adminuser ? [
'id' => (int) $location->adminuser->id,
'name'=> e($location->adminuser->present()->fullName),
]: null,
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
'parent' => ($location->parent) ? [
'id' => (int) $location->parent->id,

View File

@@ -1,43 +0,0 @@
<?php
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Database\Eloquent\Collection;
class ProfileTransformer
{
public function transformFiles(Collection $files, $total)
{
$array = [];
foreach ($files as $file) {
$array[] = self::transformFile($file);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformFile(Actionlog $file)
{
$array = [
'id' => (int) $file->id,
'icon' => Helper::filetype_icon($file->filename),
'item' => ($file->item) ? [
'name' => ($file->itemType()=='user') ? e($file->item->getFullNameAttribute()) : e($file->item->getDisplayNameAttribute()),
'type' => e($file->itemType()),
] : null,
'filename' => e($file->filename),
'signature_file' => ($file->accept_signature) ? route('profile.signature.view', ['filename' => $file->accept_signature ]) : null,
'note' => e($file->note),
'url' => route('profile.storedeula.download', ['filename' => $file->filename]),
'file' => route('profile.storedeula.download', ['filename' => $file->filename]),
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
];
return $array;
}
}

View File

@@ -45,10 +45,6 @@ class SuppliersTransformer
'components_count' => (int) $supplier->components_count,
'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedownInline($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'created_by' => $supplier->adminuser ? [
'id' => (int) $supplier->adminuser->id,
'name'=> e($supplier->adminuser->present()->fullName),
]: null,
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
];

View File

@@ -3,10 +3,10 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Models\Actionlog;
use Illuminate\Database\Eloquent\Collection;
use App\Models\Asset;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Storage;
class UploadedFilesTransformer
@@ -26,27 +26,23 @@ class UploadedFilesTransformer
{
$snipeModel = $file->item_type;
// This will be used later as we extend out this transformer to handle more types of uploads
if ($file->item_type == Asset::class) {
$file_url = route('show/assetfile', [$file->item_id, $file->id]);
}
$array = [
'id' => (int) $file->id,
'icon' => Helper::filetype_icon($file->filename),
'name' => e($file->filename),
'item' => ($file->item_type) ? [
'id' => (int) $file->item_id,
'type' => str_plural(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,
'url' => $file_url,
'created_by' => ($file->adminuser) ? [
'id' => (int) $file->adminuser->id,
'name'=> e($file->adminuser->present()->fullName),
] : null,
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
'inlineable' => StorageHelper::allowSafeInline($file->uploads_file_path()),
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
];
$permissions_array['available_actions'] = [
@@ -57,5 +53,4 @@ class UploadedFilesTransformer
return $array;
}
}
}

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,
@@ -56,16 +50,11 @@ class UsersTransformer
'id' => (int) $user->department->id,
'name'=> e($user->department->name),
] : null,
'department_manager' => ($user->department?->manager) ? [
'id' => (int) $user->department->manager->id,
'name'=> e($user->department->manager->full_name),
] : null,
'location' => ($user->userloc) ? [
'id' => (int) $user->userloc->id,
'name'=> e($user->userloc->name),
] : 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

@@ -80,16 +80,7 @@ class AssetImporter extends ItemImporter
$asset_tag = Asset::autoincrement_asset();
}
if ($this->findCsvMatch($row, 'id')!='') {
// Override asset if an ID was given
\Log::debug('Finding asset by ID: '.$this->findCsvMatch($row, 'id'));
$asset = Asset::find($this->findCsvMatch($row, 'id'));
} else {
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
}
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
if ($asset) {
if (! $this->updating) {
$exists_error = trans('general.import_asset_tag_exists', ['asset_tag' => $asset_tag]);

View File

@@ -1,99 +0,0 @@
<?php
namespace App\Importer;
use App\Models\Category;
use Illuminate\Support\Facades\Log;
/**
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
* Importer\Importer.php. [ALG]
*
* Class CategoryImporter
*/
class CategoryImporter extends ItemImporter
{
protected $categories;
public function __construct($filename)
{
parent::__construct($filename);
}
protected function handle($row)
{
parent::handle($row);
$this->createCategoryIfNotExists($row);
}
/**
* Create a category if a duplicate does not exist.
* @todo Investigate how this should interact with Importer::createCategoryIfNotExists
*
* @author A. Gianotto
* @since 6.1.0
* @param array $row
*/
public function createCategoryIfNotExists(array $row)
{
$editingCategory = false;
$category = Category::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
if ($this->findCsvMatch($row, 'id')!='') {
// Override category if an ID was given
\Log::debug('Finding category by ID: '.$this->findCsvMatch($row, 'id'));
$category = Category::find($this->findCsvMatch($row, 'id'));
}
if ($category) {
if (! $this->updating) {
$this->log('A matching Category '.$this->item['name'].' already exists');
return;
}
$this->log('Updating Category');
$editingCategory = true;
} else {
$this->log('No Matching Category, Create a new one');
$category = new Category;
$category->created_by = auth()->id();
}
// Pull the records from the CSV to determine their values
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
$this->item['eula_text'] = trim($this->findCsvMatch($row, 'eula_text'));
$this->item['category_type'] = trim(strtolower($this->findCsvMatch($row, 'category_type')));
$this->item['use_default_eula'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'use_default_eula'))) == 1) ? 1 : 0;
$this->item['require_acceptance'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'require_acceptance'))) == 1) ? 1 : 0;
$this->item['checkin_email'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'checkin_email'))) == 1) ? 1 : 0;
Log::debug('Item array is: ');
Log::debug(print_r($this->item, true));
if ($editingCategory) {
Log::debug('Updating existing category');
$category->update($this->sanitizeItemForUpdating($category));
} else {
Log::debug('Creating category');
$category->fill($this->sanitizeItemForStoring($category));
}
if ($category->save()) {
$this->log('Category '.$category->name.' created or updated from CSV import');
return $category;
} else {
Log::debug($category->getErrors());
$this->logError($category, 'Category "'.$this->item['name'].'"');
return $category->errors;
}
}
}

View File

@@ -88,7 +88,6 @@ abstract class Importer
'department' => 'department',
'manager_name' => 'manager full name',
'manager_username' => 'manager username',
'manager_employee_num' => 'manager employee number',
'min_amt' => 'minimum quantity',
'remote' => 'remote',
'vip' => 'vip',
@@ -133,7 +132,7 @@ abstract class Importer
} else {
$this->csv = Reader::createFromString($file);
}
$this->tempPassword = '*** NO PASSWORD - IMPORTED VIA CSV ***';
$this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
}
// Cached Values for import lookups

View File

@@ -110,7 +110,7 @@ class ItemImporter extends Importer
protected function determineCheckout($row)
{
// Locations don't get checked out to anyone/anything
if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class) || (get_class($this) == SupplierImporter::class) || (get_class($this) == ManufacturerImporter::class) || (get_class($this) == CategoryImporter::class)) {
if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class)) {
return;
}
@@ -353,27 +353,16 @@ class ItemImporter extends Importer
* @param $user_manager string
* @return int id of company created/found
*/
public function fetchManager($user_manager_username = null, $user_manager_employee_num = null, $user_manager_first_name = null, $user_manager_last_name = null)
public function fetchManager($user_manager_first_name, $user_manager_last_name)
{
if ($user_manager_username!='') {
$manager = User::where('username', '=', $user_manager_username)->first();
$this->log('Checking on username '.$user_manager_username);
} elseif ($user_manager_employee_num!='') {
$manager = User::where('employee_num', '=', $user_manager_employee_num)->first();
$this->log('Checking on employee_num '.$user_manager_employee_num);
} else {
$manager = User::where('first_name', '=', $user_manager_first_name)
->where('last_name', '=', $user_manager_last_name)->first();
$this->log('Checking on full name');
}
$manager = User::where('first_name', '=', $user_manager_first_name)
->where('last_name', '=', $user_manager_last_name)->first();
if ($manager) {
$this->log('A matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' already exists');
return $manager->id;
}
$this->log('No matching Manager found. If their user account is being created through this import, you should re-process this file again. ');
$this->log('No matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' found. If their user account is being created through this import, you should re-process this file again. ');
return null;
}

View File

@@ -1,101 +0,0 @@
<?php
namespace App\Importer;
use App\Models\Manufacturer;
use Illuminate\Support\Facades\Log;
/**
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
* Importer\Importer.php. [ALG]
*
* Class ManufacturerImporter
*/
class ManufacturerImporter extends ItemImporter
{
protected $manufacturers;
public function __construct($filename)
{
parent::__construct($filename);
}
protected function handle($row)
{
parent::handle($row);
$this->createManufacturerIfNotExists($row);
}
/**
* Create a supplier if a duplicate does not exist.
* @todo Investigate how this should interact with Importer::createManufacturerIfNotExists
*
* @author A. Gianotto
* @since 6.1.0
* @param array $row
*/
public function createManufacturerIfNotExists(array $row)
{
$editingManufacturer = false;
$supplier = Manufacturer::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
if ($this->findCsvMatch($row, 'id')!='') {
// Override supplier if an ID was given
\Log::debug('Finding supplier by ID: '.$this->findCsvMatch($row, 'id'));
$supplier = Manufacturer::find($this->findCsvMatch($row, 'id'));
}
if ($supplier) {
if (! $this->updating) {
$this->log('A matching Manufacturer '.$this->item['name'].' already exists');
return;
}
$this->log('Updating Manufacturer');
$editingManufacturer = true;
} else {
$this->log('No Matching Manufacturer, Create a new one');
$supplier = new Manufacturer;
$supplier->created_by = auth()->id();
}
// Pull the records from the CSV to determine their values
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
$this->item['support_phone'] = trim($this->findCsvMatch($row, 'support_phone'));
$this->item['fax'] = trim($this->findCsvMatch($row, 'fax'));
$this->item['support_email'] = trim($this->findCsvMatch($row, 'support_email'));
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
$this->item['support_url'] = trim($this->findCsvMatch($row, 'support_url'));
$this->item['warranty_lookup_url'] = trim($this->findCsvMatch($row, 'warranty_lookup_url'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
Log::debug('Item array is: ');
Log::debug(print_r($this->item, true));
if ($editingManufacturer) {
Log::debug('Updating existing supplier');
$supplier->update($this->sanitizeItemForUpdating($supplier));
} else {
Log::debug('Creating supplier');
$supplier->fill($this->sanitizeItemForStoring($supplier));
}
if ($supplier->save()) {
$this->log('Manufacturer '.$supplier->name.' created or updated from CSV import');
return $supplier;
} else {
Log::debug($supplier->getErrors());
$this->logError($supplier, 'Manufacturer "'.$this->item['name'].'"');
return $supplier->errors;
}
}
}

View File

@@ -1,105 +0,0 @@
<?php
namespace App\Importer;
use App\Models\Supplier;
use Illuminate\Support\Facades\Log;
/**
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
* Importer\Importer.php. [ALG]
*
* Class SupplierImporter
*/
class SupplierImporter extends ItemImporter
{
protected $suppliers;
public function __construct($filename)
{
parent::__construct($filename);
}
protected function handle($row)
{
parent::handle($row);
$this->createSupplierIfNotExists($row);
}
/**
* Create a supplier if a duplicate does not exist.
* @todo Investigate how this should interact with Importer::createSupplierIfNotExists
*
* @author A. Gianotto
* @since 6.1.0
* @param array $row
*/
public function createSupplierIfNotExists(array $row)
{
$editingSupplier = false;
$supplier = Supplier::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
if ($this->findCsvMatch($row, 'id')!='') {
// Override supplier if an ID was given
\Log::debug('Finding supplier by ID: '.$this->findCsvMatch($row, 'id'));
$supplier = Supplier::find($this->findCsvMatch($row, 'id'));
}
if ($supplier) {
if (! $this->updating) {
$this->log('A matching Supplier '.$this->item['name'].' already exists');
return;
}
$this->log('Updating Supplier');
$editingSupplier = true;
} else {
$this->log('No Matching Supplier, Create a new one');
$supplier = new Supplier;
$supplier->created_by = auth()->id();
}
// Pull the records from the CSV to determine their values
$this->item['name'] = trim($this->findCsvMatch($row, 'name'));
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));
$this->item['address2'] = trim($this->findCsvMatch($row, 'address2'));
$this->item['city'] = trim($this->findCsvMatch($row, 'city'));
$this->item['state'] = trim($this->findCsvMatch($row, 'state'));
$this->item['country'] = trim($this->findCsvMatch($row, 'country'));
$this->item['zip'] = trim($this->findCsvMatch($row, 'zip'));
$this->item['phone'] = trim($this->findCsvMatch($row, 'phone'));
$this->item['fax'] = trim($this->findCsvMatch($row, 'fax'));
$this->item['email'] = trim($this->findCsvMatch($row, 'email'));
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
Log::debug('Item array is: ');
Log::debug(print_r($this->item, true));
if ($editingSupplier) {
Log::debug('Updating existing supplier');
$supplier->update($this->sanitizeItemForUpdating($supplier));
} else {
Log::debug('Creating supplier');
$supplier->fill($this->sanitizeItemForStoring($supplier));
}
if ($supplier->save()) {
$this->log('Supplier '.$supplier->name.' created or updated from CSV import');
return $supplier;
} else {
Log::debug($supplier->getErrors());
$this->logError($supplier, 'Supplier "'.$this->item['name'].'"');
return $supplier->errors;
}
}
}

View File

@@ -7,9 +7,7 @@ use App\Models\Department;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
/**
* This is ONLY used for the User Import. When we are importing users
@@ -64,7 +62,7 @@ class UserImporter extends ItemImporter
$this->item['activated'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'activated'))) == 1) ? '1' : 0;
$this->item['employee_num'] = trim($this->findCsvMatch($row, 'employee_num'));
$this->item['department_id'] = trim($this->createOrFetchDepartment(trim($this->findCsvMatch($row, 'department'))));
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_username')), trim($this->findCsvMatch($row, 'manager_employee_num')), trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
$this->item['remote'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'remote'))) == 1 ) ? '1' : 0;
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;
@@ -82,7 +80,6 @@ class UserImporter extends ItemImporter
$this->item['username'] = $user_formatted_array['username'];
}
// Check if a numeric ID was passed. If it does, use that above all else.
if ((array_key_exists('id', $this->item) && ($this->item['id'] != "") && (is_numeric($this->item['id'])))) {
$user = User::find($this->item['id']);
@@ -92,25 +89,12 @@ class UserImporter extends ItemImporter
if ($user) {
// If the user does not want to update existing values, only add new ones, bail out
if (! $this->updating) {
Log::debug('A matching User '.$this->item['name'].' already exists. ');
return;
}
$this->log('Updating User');
// Todo - check that this works
if (!Gate::allows('canEditAuthFields', $user)) {
unset($user->username);
unset($user->email);
unset($user->password);
unset($user->activated);
}
$user->update($this->sanitizeItemForUpdating($user));
// Why do we have to do this twice? Update should
$user->save();
// Update the location of any assets checked out to this user
@@ -126,32 +110,28 @@ class UserImporter extends ItemImporter
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords
// Issue #5408
$this->item['password'] = $this->tempPassword;
$this->item['password'] = bcrypt($this->tempPassword);
$this->log('No matching user, creating one');
$user = new User();
$user->created_by = auth()->id();
$user->fill($this->sanitizeItemForStoring($user));
// TODO - check for gate here I guess
if ($user->save()) {
$this->log('User '.$this->item['name'].' was created');
if (($user->email) && ($user->activated == '1')) {
$data = [
'email' => $user->email,
'username' => $user->username,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'password' => $this->tempPassword,
];
if ($this->send_welcome) {
try {
$user->notify(new WelcomeNotification($user));
} catch (\Exception $e) {
Log::warning('Could not send welcome notification for user: ' . $e->getMessage());
}
$user->notify(new WelcomeNotification($data));
}
}
$user = null;
$this->item = null;
@@ -160,9 +140,9 @@ class UserImporter extends ItemImporter
}
$this->logError($user, 'User');
return;
}
/**
* Fetch an existing department, or create new if it doesn't exist
*

View File

@@ -4,17 +4,14 @@ namespace App\Listeners;
use App\Events\CheckoutableCheckedOut;
use App\Mail\CheckinAccessoryMail;
use App\Mail\CheckinComponentMail;
use App\Mail\CheckinLicenseMail;
use App\Mail\CheckoutAccessoryMail;
use App\Mail\CheckoutAssetMail;
use App\Mail\CheckinAssetMail;
use App\Mail\CheckoutComponentMail;
use App\Mail\CheckoutConsumableMail;
use App\Mail\CheckoutLicenseMail;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Category;
use App\Models\CheckoutAcceptance;
use App\Models\Component;
use App\Models\Consumable;
@@ -24,11 +21,9 @@ use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckinAccessoryNotification;
use App\Notifications\CheckinAssetNotification;
use App\Notifications\CheckinComponentNotification;
use App\Notifications\CheckinLicenseSeatNotification;
use App\Notifications\CheckoutAccessoryNotification;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CheckoutComponentNotification;
use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseSeatNotification;
use GuzzleHttp\Exception\ClientException;
@@ -43,7 +38,7 @@ use Osama\LaravelTeamsNotification\TeamsNotification;
class CheckoutableListener
{
private array $skipNotificationsFor = [
// Component::class,
Component::class,
];
/**
@@ -74,16 +69,16 @@ class CheckoutableListener
return;
}
$acceptance = $this->getCheckoutAcceptance($event);
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($event->checkoutable);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress($acceptance);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
return;
}
$acceptance = $this->getCheckoutAcceptance($event);
if ($shouldSendEmailToUser || $shouldSendEmailToAlertAddress) {
$mailable = $this->getCheckoutMailType($event, $acceptance);
$notifiable = $this->getNotifiableUser($event);
@@ -149,6 +144,7 @@ class CheckoutableListener
$shouldSendEmailToUser = $this->checkoutableCategoryShouldSendEmail($event->checkoutable);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
return;
}
@@ -231,7 +227,6 @@ class CheckoutableListener
if ($checkedOutToType != "App\Models\User") {
return null;
}
if (!$event->checkoutable->requireAcceptance()) {
return null;
}
@@ -239,13 +234,6 @@ class CheckoutableListener
$acceptance = new CheckoutAcceptance;
$acceptance->checkoutable()->associate($event->checkoutable);
$acceptance->assignedTo()->associate($event->checkedOutTo);
$category = $this->getCategoryFromCheckoutable($event->checkoutable);
if ($category?->alert_on_response) {
$acceptance->alert_on_response_id = auth()->id();
}
$acceptance->save();
return $acceptance;
@@ -272,9 +260,6 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckinLicenseSeatNotification::class;
break;
case Component::class:
$notificationClass = CheckinComponentNotification::class;
break;
}
Log::debug('Notification class: '.$notificationClass);
@@ -305,9 +290,6 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckoutLicenseSeatNotification::class;
break;
case Component::class:
$notificationClass = CheckoutComponentNotification::class;
break;
}
@@ -319,7 +301,6 @@ class CheckoutableListener
Asset::class => CheckoutAssetMail::class,
LicenseSeat::class => CheckoutLicenseMail::class,
Consumable::class => CheckoutConsumableMail::class,
Component::class => CheckoutComponentMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
@@ -332,8 +313,8 @@ class CheckoutableListener
Accessory::class => CheckinAccessoryMail::class,
Asset::class => CheckinAssetMail::class,
LicenseSeat::class => CheckinLicenseMail::class,
Component::class => CheckinComponentMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note);
@@ -379,6 +360,23 @@ class CheckoutableListener
return in_array(get_class($checkoutable), $this->skipNotificationsFor);
}
private function shouldSendEmailNotifications(Model $checkoutable): bool
{
//runs a check if the category wants to send checkin/checkout emails to users
$category = match (true) {
$checkoutable instanceof Asset => $checkoutable->model->category,
$checkoutable instanceof Accessory,
$checkoutable instanceof Consumable => $checkoutable->category,
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
default => null,
};
if (!$category?->checkin_email) {
return false;
}
return true;
}
private function shouldSendWebhookNotification(): bool
{
return Setting::getSettings() && Setting::getSettings()->webhook_endpoint;
@@ -421,19 +419,9 @@ class CheckoutableListener
return false;
}
private function shouldSendEmailToAlertAddress($acceptance = null): bool
private function shouldSendEmailToAlertAddress(): bool
{
$setting = Setting::getSettings();
if (!$setting) {
return false;
}
if (is_null($acceptance) && !$setting->admin_cc_always) {
return false;
}
return (bool) $setting->admin_cc_email;
return Setting::getSettings() && Setting::getSettings()->admin_cc_email;
}
private function getFormattedAlertAddresses(): array
@@ -473,15 +461,4 @@ class CheckoutableListener
return array($to, $cc);
}
private function getCategoryFromCheckoutable(Model $checkoutable): ?Category
{
return match (true) {
$checkoutable instanceof Asset => $checkoutable->model->category,
$checkoutable instanceof Accessory,
$checkoutable instanceof Consumable,
$checkoutable instanceof Component => $checkoutable->category,
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
};
}
}

View File

@@ -6,8 +6,6 @@ use Livewire\Component;
class CategoryEditForm extends Component
{
public bool $alertOnResponse;
public $defaultEulaText;
public $eulaText;

View File

@@ -35,14 +35,10 @@ class Importer extends Component
public $accessories_fields;
public $assets_fields;
public $users_fields;
public $assetmodels_fields;
public $suppliers_fields;
public $licenses_fields;
public $locations_fields;
public $consumables_fields;
public $components_fields;
public $manufacturers_fields;
public $categories_fields;
public $aliases_fields;
protected $rules = [
@@ -89,6 +85,9 @@ class Importer extends Component
case 'component':
$results = $this->components_fields;
break;
case 'consumable':
$results = $this->consumables_fields;
break;
case 'license':
$results = $this->licenses_fields;
break;
@@ -98,14 +97,8 @@ class Importer extends Component
case 'location':
$results = $this->locations_fields;
break;
case 'supplier':
$results = $this->suppliers_fields;
break;
case 'manufacturer':
$results = $this->manufacturers_fields;
break;
case 'category':
$results = $this->categories_fields;
case 'user':
$results = $this->users_fields;
break;
default:
$results = [];
@@ -135,7 +128,7 @@ class Importer extends Component
//yes, this key *is* valid. Continue on to the next field.
continue;
} else {
//no, this key is *INVALID* for this import type. Better set it to null,
//no, this key is *INVALID* for this import type. Better set it to null
// and we'll hope that the $aliases_fields or something else picks it up.
$this->field_map[$i] = null; // fingers crossed! But it's not likely, tbh.
} // TODO - strictly speaking, this isn't necessary here I don't think.
@@ -156,7 +149,7 @@ class Importer extends Component
// in "Accessories"!)
if (array_key_exists($key, $this->columnOptions[$type])) {
$this->field_map[$i] = $key;
continue 3; // bust out of both of these loops and the surrounding one - e.g. move on to the next header
continue 3; // bust out of both of these loops; as well as the surrounding one - e.g. move on to the next header
}
}
}
@@ -178,9 +171,6 @@ class Importer extends Component
'license' => trans('general.licenses'),
'location' => trans('general.locations'),
'user' => trans('general.users'),
'supplier' => trans('general.suppliers'),
'manufacturer' => trans('general.manufacturers'),
'category' => trans('general.categories'),
];
/**
@@ -203,7 +193,6 @@ class Importer extends Component
];
$this->assets_fields = [
'id' => trans('general.id'),
'asset_eol_date' => trans('admin/hardware/form.eol_date'),
'asset_model' => trans('general.model_name'),
'asset_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/general.asset')]),
@@ -330,8 +319,6 @@ class Importer extends Component
'location' => trans('general.location'),
'manager_first_name' => trans('general.importer.manager_first_name'),
'manager_last_name' => trans('general.importer.manager_last_name'),
'manager_employee_num' => trans('general.importer.manager_employee_num'),
'manager_username' => trans('general.importer.manager_username'),
'notes' => trans('general.notes'),
'phone_number' => trans('admin/users/table.phone'),
'remote' => trans('admin/users/general.remote'),
@@ -345,7 +332,6 @@ class Importer extends Component
$this->locations_fields = [
'id' => trans('general.id'),
'name' => trans('general.name'),
'address' => trans('general.address'),
'address2' => trans('general.importer.address2'),
'city' => trans('general.city'),
@@ -354,52 +340,13 @@ class Importer extends Component
'ldap_ou' => trans('admin/locations/table.ldap_ou'),
'manager' => trans('general.importer.manager_full_name'),
'manager_username' => trans('general.importer.manager_username'),
'name' => trans('general.item_name_var', ['item' => trans('general.location')]),
'notes' => trans('general.notes'),
'parent_location' => trans('admin/locations/table.parent'),
'state' => trans('general.state'),
'zip' => trans('general.zip'),
];
$this->suppliers_fields = [
'id' => trans('general.id'),
'name' => trans('general.name'),
'address' => trans('general.address'),
'address2' => trans('general.importer.address2'),
'city' => trans('general.city'),
'notes' => trans('general.notes'),
'state' => trans('general.state'),
'zip' => trans('general.zip'),
'phone' => trans('general.phone'),
'fax' => trans('general.fax'),
'url' => trans('general.url'),
'contact' => trans('general.contact'),
'email' => trans('general.email'),
];
$this->manufacturers_fields = [
'id' => trans('general.id'),
'name' => trans('general.name'),
'notes' => trans('general.notes'),
'support_phone' => trans('admin/manufacturers/table.support_phone'),
'support_url' => trans('admin/manufacturers/table.support_url'),
'support_email' => trans('admin/manufacturers/table.support_email'),
'warranty_lookup_url' => trans('admin/manufacturers/table.warranty_lookup_url'),
'url' => trans('general.url'),
];
$this->categories_fields = [
'id' => trans('general.id'),
'name' => trans('general.name'),
'notes' => trans('general.notes'),
'category_type' => trans('admin/categories/general.import_category_type'),
'eula_text' => trans('admin/categories/general.import_eula_text'),
'use_default_eula' => trans('admin/categories/general.use_default_eula_column'),
'require_acceptance' => trans('admin/categories/general.import_require_acceptance'),
'checkin_email' => trans('admin/categories/general.import_checkin_email'),
];
$this->assetmodels_fields = [
'category' => trans('general.category'),
'eol' => trans('general.eol'),
@@ -424,8 +371,6 @@ class Importer extends Component
'consumable name',
'component name',
'name',
'supplier name',
'location name',
],
'item_no' => [
'item number',

View File

@@ -10,16 +10,15 @@ class LocationScopeCheck extends Component
{
public $mismatched = [];
public $setting;
public $is_tested = false;
public function check_locations()
{
$this->mismatched = Helper::test_locations_fmcs(false);
$this->is_tested = true;
}
public function mount() {
$this->setting = Setting::getSettings();
$this->mismatched = Helper::test_locations_fmcs(false);
}
public function render()

View File

@@ -1,71 +0,0 @@
<?php
namespace App\Mail;
use App\Models\Accessory;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckinComponentMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Component $component, $checkedOutTo, User $checkedInby, $note)
{
$this->item = $component;
$this->target = $checkedOutTo;
$this->admin = $checkedInby;
$this->note = $note;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_component_checkin'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkin-component',
with: [
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace App\Mail;
use App\Models\CheckoutAcceptance;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckoutAcceptanceResponseMail extends Mailable
{
use Queueable, SerializesModels;
public CheckoutAcceptance $acceptance;
public User $recipient;
public bool $wasAccepted;
/**
* Create a new message instance.
*/
public function __construct(CheckoutAcceptance $acceptance, User $recipient, bool $wasAccepted)
{
$this->acceptance = $acceptance;
$this->recipient = $recipient;
$this->wasAccepted = $wasAccepted;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$subject = $this->wasAccepted
? trans('mail.initiated_accepted')
: trans('mail.initiated_declined');
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkout-acceptance-response',
with: [
'assignedTo' => $this->acceptance->assignedTo,
'introduction' => $this->introduction(),
'item' => $this->acceptance->checkoutable,
'note' => $this->acceptance->note,
'recipient' => $this->recipient,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
private function introduction(): string
{
return $this->wasAccepted
? trans('mail.following_accepted')
: trans('mail.following_declined');
}
}

View File

@@ -1,82 +0,0 @@
<?php
namespace App\Mail;
use App\Models\Component;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckoutComponentMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(Component $component, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
{
$this->item = $component;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $component->assets->first()?->pivot?->assigned_qty;
$this->settings = Setting::getSettings();
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans('mail.Confirm_component_delivery'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$eula = $this->item->getEula();
$req_accept = $this->item->requireAcceptance();
$accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance);
return new Content(
markdown: 'mail.markdown.checkout-component',
with: [
'item' => $this->item,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'qty' => $this->qty,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -4,7 +4,6 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -15,7 +14,7 @@ use Watson\Validating\ValidatingTrait;
/**
* Model for Accessories.
*
* @version v1.0
* @version v1.0
*/
class Accessory extends SnipeModel
{
@@ -23,7 +22,6 @@ class Accessory extends SnipeModel
protected $presenter = \App\Presenters\AccessoryPresenter::class;
use CompanyableTrait;
use HasUploads;
use Loggable, Presentable;
use SoftDeletes;
@@ -56,8 +54,8 @@ class Accessory extends SnipeModel
];
/**
* Accessory validation rules
*/
* Accessory validation rules
*/
public $rules = [
'name' => 'required|min:3|max:255',
'qty' => 'required|integer|min:1',
@@ -71,12 +69,12 @@ class Accessory extends SnipeModel
/**
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* @var bool
*/
*/
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
@@ -104,11 +102,29 @@ class Accessory extends SnipeModel
];
/**
* Establishes the accessories -> action logs -> uploads relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
->where('item_type', '=', self::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
* Establishes the accessory -> supplier relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
@@ -121,7 +137,7 @@ class Accessory extends SnipeModel
* Sets the requestable attribute on the accessory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @since [v4.0]
* @return void
*/
public function setRequestableAttribute($value)
@@ -136,7 +152,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> company relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -148,7 +164,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> location relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -160,7 +176,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> category relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -172,7 +188,7 @@ class Accessory extends SnipeModel
* Returns the action logs associated with the accessory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
@@ -201,8 +217,8 @@ class Accessory extends SnipeModel
*
* It's super-mega-assy, but it's the best I could do for now.
*
* @author A. Gianotto <snipe@snipe.net>
* @since v5.0.0
* @author A. Gianotto <snipe@snipe.net>
* @since v5.0.0
*
* @see \App\Http\Controllers\Api\AccessoriesController\checkedout()
*/
@@ -219,7 +235,7 @@ class Accessory extends SnipeModel
* presenter or service provider
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return string
*/
public function getImageUrl()
@@ -235,7 +251,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> users relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function checkouts()
@@ -248,7 +264,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> admin user relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v7.0.13]
* @since [v7.0.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -260,7 +276,7 @@ class Accessory extends SnipeModel
* Checks whether or not the accessory has users
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return int
*/
public function hasUsers()
@@ -274,7 +290,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> manufacturer relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -287,12 +303,12 @@ class Accessory extends SnipeModel
* accessory based on the category it belongs to.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return bool
*/
public function checkin_email()
{
return $this->category?->checkin_email;
return $this->category->checkin_email;
}
/**
@@ -300,7 +316,7 @@ class Accessory extends SnipeModel
* accept it via email.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return bool
*/
public function requireAcceptance()
@@ -313,7 +329,7 @@ class Accessory extends SnipeModel
* checks for a settings level EULA
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return string
*/
public function getEula()
@@ -333,7 +349,7 @@ class Accessory extends SnipeModel
* Check how many items within an accessory are checked out
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.0]
* @since [v5.0]
* @return int
*/
public function numCheckedOut()
@@ -350,7 +366,7 @@ class Accessory extends SnipeModel
* bad things happen.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return int
*/
public function numRemaining()
@@ -365,8 +381,8 @@ class Accessory extends SnipeModel
/**
* Run after the checkout acceptance was declined by the user
*
* @param User $acceptedBy
* @param string $signature
* @param User $acceptedBy
* @param string $signature
*/
public function declinedCheckout(User $declinedBy, $signature)
{
@@ -392,8 +408,8 @@ class Accessory extends SnipeModel
* This simply checks that there is a value for quantity, and if there isn't, set it to 0.
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.3.4
* @param $value
* @since v6.3.4
* @param $value
* @return void
*/
public function setQtyAttribute($value)
@@ -410,6 +426,7 @@ class Accessory extends SnipeModel
/**
* Query builder scope to order on created_by name
*
*/
public function scopeOrderByCreatedByName($query, $order)
{
@@ -417,68 +434,68 @@ class Accessory extends SnipeModel
}
/**
* Query builder scope to order on company
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
* Query builder scope to order on company
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderCompany($query, $order)
{
return $query->leftJoin('companies', 'accessories.company_id', '=', 'companies.id')
->orderBy('companies.name', $order);
->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on category
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
* Query builder scope to order on category
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderCategory($query, $order)
{
return $query->leftJoin('categories', 'accessories.category_id', '=', 'categories.id')
->orderBy('categories.name', $order);
->orderBy('categories.name', $order);
}
/**
* Query builder scope to order on location
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
* Query builder scope to order on location
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderLocation($query, $order)
{
return $query->leftJoin('locations', 'accessories.location_id', '=', 'locations.id')
->orderBy('locations.name', $order);
->orderBy('locations.name', $order);
}
/**
* Query builder scope to order on manufacturer
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
* Query builder scope to order on manufacturer
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderManufacturer($query, $order)
{
return $query->leftJoin('manufacturers', 'accessories.manufacturer_id', '=', 'manufacturers.id')->orderBy('manufacturers.name', $order);
}
/**
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
* Query builder scope to order on supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderSupplier($query, $order)
{
return $query->leftJoin('suppliers', 'accessories.supplier_id', '=', 'suppliers.id')->orderBy('suppliers.name', $order);

View File

@@ -16,7 +16,7 @@ use Watson\Validating\ValidatingTrait;
/**
* Model for Accessories.
*
* @version v1.0
* @version v1.0
*/
class AccessoryCheckout extends Model
{
@@ -36,7 +36,7 @@ class AccessoryCheckout extends Model
* Establishes the accessory checkout -> accessory relationship
*
* @author [A. Kroeger]
* @since [v7.0.9]
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessory()
@@ -52,7 +52,7 @@ class AccessoryCheckout extends Model
* Establishes the accessory checkout -> user relationship
*
* @author [A. Kroeger]
* @since [v7.0.9]
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -64,7 +64,7 @@ class AccessoryCheckout extends Model
* Get the target this asset is checked out to
*
* @author [A. Kroeger]
* @since [v7.0]
* @since [v7.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedTo()
@@ -76,7 +76,7 @@ class AccessoryCheckout extends Model
* Gets the lowercased name of the type of target the asset is assigned to
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @since [v4.0]
* @return string
*/
public function assignedType()
@@ -91,7 +91,7 @@ class AccessoryCheckout extends Model
* this method is an easy way of seeing if we are checked out to a user.
*
* @author [A. Kroeger]
* @since [v7.0]
* @since [v7.0]
*/
public function checkedOutToUser(): bool
{
@@ -127,64 +127,50 @@ class AccessoryCheckout extends Model
* Run additional, advanced searches.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $terms The search terms
* @param array $terms The search terms
* @return \Illuminate\Database\Eloquent\Builder
*/
public function advancedTextSearch(Builder $query, array $terms)
{
$userQuery = User::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
}
$userQuery = User::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
}
)->select('id');
})->select('id');
$locationQuery = Location::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
$locationQuery = Location::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
)->select('id');
})->select('id');
$assetQuery = Asset::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
$assetQuery = Asset::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
)->select('id');
})->select('id');
$query->where(
function ($query) use ($userQuery) {
$query->where('assigned_type', User::class)
->whereIn('assigned_to', $userQuery);
$query->where(function ($query) use ($userQuery) {
$query->where('assigned_type', User::class)
->whereIn('assigned_to', $userQuery);
})->orWhere(function($query) use ($locationQuery) {
$query->where('assigned_type', Location::class)
->whereIn('assigned_to', $locationQuery);
})->orWhere(function($query) use ($assetQuery) {
$query->where('assigned_type', Asset::class)
->whereIn('assigned_to', $assetQuery);
})->orWhere(function($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('note', 'like', $search_str);
}
)->orWhere(
function ($query) use ($locationQuery) {
$query->where('assigned_type', Location::class)
->whereIn('assigned_to', $locationQuery);
}
)->orWhere(
function ($query) use ($assetQuery) {
$query->where('assigned_type', Asset::class)
->whereIn('assigned_to', $assetQuery);
}
)->orWhere(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('note', 'like', $search_str);
}
}
);
});
return $query;
}

View File

@@ -12,7 +12,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* Model for the Actionlog (the table that keeps a historical log of
* checkouts, checkins, and updates).
*
* @version v1.0
* @version v1.0
*/
class Actionlog extends SnipeModel
{
@@ -69,13 +69,11 @@ class Actionlog extends SnipeModel
*/
protected $searchableRelations = [
'company' => ['name'],
'adminuser' => ['first_name','last_name','username', 'email', 'employee_num'],
'user' => ['first_name','last_name','username', 'email', 'employee_num'],
'adminuser' => ['first_name','last_name','username', 'email'],
'user' => ['first_name','last_name','username', 'email'],
'assets' => ['asset_tag','name', 'serial', 'order_number', 'notes', 'purchase_date'],
'assets.model' => ['name', 'model_number', 'eol', 'notes'],
'assets.model.category' => ['name', 'notes'],
'assets.location' => ['name'],
'assets.defaultLoc' => ['name'],
'assets.model.manufacturer' => ['name', 'notes'],
'licenses' => ['name', 'serial', 'notes', 'order_number', 'license_email', 'license_name', 'purchase_order', 'purchase_date'],
'licenses.category' => ['name', 'notes'],
@@ -98,31 +96,24 @@ class Actionlog extends SnipeModel
* Override from Builder to automatically add the company
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public static function boot()
{
parent::boot();
static::creating(
function (self $actionlog) {
// If the admin is a superadmin, let's see if the target instead has a company.
if (auth()->user() && auth()->user()->isSuperUser()) {
if ($actionlog->target) {
$actionlog->company_id = $actionlog->target->company_id;
} elseif ($actionlog->item) {
$actionlog->company_id = $actionlog->item->company_id;
}
} elseif (auth()->user() && auth()->user()->company) {
$actionlog->company_id = auth()->user()->company_id;
static::creating(function (self $actionlog) {
// If the admin is a superadmin, let's see if the target instead has a company.
if (auth()->user() && auth()->user()->isSuperUser()) {
if ($actionlog->target) {
$actionlog->company_id = $actionlog->target->company_id;
} elseif ($actionlog->item) {
$actionlog->company_id = $actionlog->item->company_id;
}
if ($actionlog->action_date == '') {
$actionlog->action_date = Carbon::now();
}
} elseif (auth()->user() && auth()->user()->company) {
$actionlog->company_id = auth()->user()->company_id;
}
);
});
}
@@ -130,7 +121,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> item relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function item()
@@ -142,7 +133,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> company relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -155,7 +146,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> asset relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -167,7 +158,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> license relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -179,7 +170,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> consumable relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
@@ -191,7 +182,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> consumable relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessories()
@@ -203,7 +194,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> components relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
@@ -215,7 +206,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> item type relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function itemType()
@@ -231,7 +222,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> target type relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function targetType()
@@ -248,21 +239,21 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> uploads relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->morphTo('item')
->where('action_type', '=', 'uploaded')
->withTrashed();
->where('action_type', '=', 'uploaded')
->withTrashed();
}
/**
* Establishes the actionlog -> userlog relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function userlog()
@@ -274,20 +265,20 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> admin user relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
{
return $this->belongsTo(User::class, 'created_by')
->withTrashed();
->withTrashed();
}
/**
* Establishes the actionlog -> user relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
@@ -300,7 +291,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> target relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function target()
@@ -312,7 +303,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> location relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -325,7 +316,7 @@ class Actionlog extends SnipeModel
* Check if the file exists, and if it does, force a download
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return string | false
*/
public function get_src($type = 'assets', $fieldname = 'filename')
@@ -343,7 +334,7 @@ class Actionlog extends SnipeModel
* Saves the log record with the action type
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return bool
*/
public function logaction($actiontype)
@@ -364,7 +355,7 @@ class Actionlog extends SnipeModel
* Calculate the number of days until the next audit
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @since [v4.0]
* @return int
*/
public function daysUntilNextAudit($monthInterval = 12, $asset = null)
@@ -385,7 +376,7 @@ class Actionlog extends SnipeModel
if ($this->created_at > $override_default_next) {
$next_audit_days = '-'.$next_audit_days;
}
return $next_audit_days;
}
@@ -393,7 +384,7 @@ class Actionlog extends SnipeModel
* Calculate the date of the next audit
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @since [v4.0]
* @return \Datetime
*/
public function calcNextAuditDate($monthInterval = 12, $asset = null)
@@ -410,24 +401,24 @@ class Actionlog extends SnipeModel
/**
* Gets action logs in chronological order, excluding uploads
*
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @since v1.0
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @since v1.0
* @return \Illuminate\Database\Eloquent\Collection
*/
public function getListingOfActionLogsChronologicalOrder()
{
return $this->all()
->where('action_type', '!=', 'uploaded')
->orderBy('item_id', 'asc')
->orderBy('created_at', 'asc')
->get();
->where('action_type', '!=', 'uploaded')
->orderBy('item_id', 'asc')
->orderBy('created_at', 'asc')
->get();
}
/**
* Determines what the type of request is so we can log it to the action_log
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.3.0
* @since v6.3.0
* @return string
*/
public function determineActionSource(): string
@@ -439,12 +430,11 @@ class Actionlog extends SnipeModel
// This is an API call
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
&& (starts_with(request()->header('authorization'), 'Bearer '))
) {
&& (starts_with(request()->header('authorization'), 'Bearer '))) {
return 'api';
}
// This is probably NOT an API call
// This is probably NOT an API call
if (request()->filled('_token')) {
return 'gui';
}
@@ -454,71 +444,6 @@ 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.consumablefile', [$this->item_id, $this->id]);
case Component::class:
return route('show.componentfile', [$this->item_id, $this->id]);
case License::class:
return route('show.licensefile', [$this->item_id, $this->id]);
case Location::class:
return route('show/locationsfile', [$this->item_id, $this->id]);
case User::class:
return route('show/userfile', [$this->item_id, $this->id]);
default:
return null;
}
}
public function uploads_file_path()
{
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/assetmodels/'.$this->filename;
case Consumable::class:
return 'private_uploads/consumables/'.$this->filename;
case Component::class:
return 'private_uploads/components/'.$this->filename;
case License::class:
return 'private_uploads/licenses/'.$this->filename;
case Location::class:
return 'private_uploads/locations/'.$this->filename;
case User::class:
return 'private_uploads/users/'.$this->filename;
default:
return null;
}
}
// Manually sets $this->source for determineActionSource()
public function setActionSource($source = null): void
{
@@ -529,4 +454,4 @@ class Actionlog extends SnipeModel
{
return $query->leftJoin('users as admin_sort', 'action_logs.created_by', '=', 'admin_sort.id')->select('action_logs.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ use Watson\Validating\ValidatingTrait;
/**
* Model for Asset Maintenances.
*
* @version v1.0
* @version v1.0
*/
class AssetMaintenance extends Model implements ICompanyableChild
{
@@ -26,12 +26,12 @@ class AssetMaintenance extends Model implements ICompanyableChild
protected $table = 'asset_maintenances';
protected $rules = [
'asset_id' => 'required|integer',
'supplier_id' => 'nullable|integer',
'supplier_id' => 'required|integer',
'asset_maintenance_type' => 'required',
'title' => 'required|max:100',
'is_warranty' => 'boolean',
'start_date' => 'required|date_format:Y-m-d',
'completion_date' => 'date_format:Y-m-d|nullable|after_or_equal:start_date',
'completion_date' => 'date_format:Y-m-d|nullable',
'notes' => 'string|nullable',
'cost' => 'numeric|nullable',
];
@@ -93,7 +93,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* getImprovementOptions
*
* @return array
* @return array
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
*/
@@ -157,21 +157,20 @@ class AssetMaintenance extends Model implements ICompanyableChild
* asset
* Get asset for this improvement
*
* @return mixed
* @return mixed
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
*/
public function asset()
{
return $this->belongsTo(\App\Models\Asset::class, 'asset_id')
->withTrashed();
->withTrashed();
}
/**
* Get the admin who created the maintenance
*
* @return mixed
* @return mixed
* @author A. Gianotto <snipe@snipe.net>
* @version v3.0
*/
@@ -184,7 +183,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
public function supplier()
{
return $this->belongsTo(\App\Models\Supplier::class, 'supplier_id')
->withTrashed();
->withTrashed();
}
/**
@@ -196,8 +195,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* Query builder scope to order on a supplier
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -212,8 +211,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* Query builder scope to order on asset tag
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -226,8 +225,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* Query builder scope to order on asset tag
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -240,8 +239,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* Query builder scope to order on serial
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param string $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -254,8 +253,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
/**
* Query builder scope to order on status label name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -266,21 +265,6 @@ class AssetMaintenance extends Model implements ICompanyableChild
->orderBy('maintained_asset_status.name', $order);
}
/**
* Query builder scope to order on status label name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderLocationName($query, $order)
{
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
->leftjoin('locations as maintained_asset_location', 'maintained_asset_location.id', '=', 'maintained_asset.location_id')
->orderBy('maintained_asset_location.name', $order);
}
/**
* Query builder scope to order on the user that created it
*/

View File

@@ -2,7 +2,6 @@
namespace App\Models;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -17,7 +16,7 @@ use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
* Model for Asset Models. Asset Models contain higher level
* attributes that are common among the same type of asset.
*
* @version v1.0
* @version v1.0
*/
class AssetModel extends SnipeModel
{
@@ -25,7 +24,6 @@ class AssetModel extends SnipeModel
use SoftDeletes;
use Loggable, Requestable, Presentable;
use TwoColumnUniqueUndeletedTrait;
use HasUploads;
/**
* Whether the model should inject its identifier to the unique
@@ -105,7 +103,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> assets relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -117,7 +115,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> category relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -129,7 +127,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> depreciation relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function depreciation()
@@ -141,7 +139,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> manufacturer relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -153,7 +151,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> fieldset relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function fieldset()
@@ -163,14 +161,14 @@ class AssetModel extends SnipeModel
public function customFields()
{
return $this->fieldset()->first()->fields();
return $this->fieldset()->first()->fields();
}
/**
* Establishes the model -> custom field default values relationship
*
* @author hannah tinkler
* @since [v4.3]
* @since [v4.3]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function defaultValues()
@@ -184,7 +182,7 @@ class AssetModel extends SnipeModel
* @todo this should probably be moved
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function getImageUrl()
@@ -201,7 +199,7 @@ class AssetModel extends SnipeModel
* Checks if the model is deletable
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.3.4]
* @since [v6.3.4]
* @return bool
*/
public function isDeletable()
@@ -211,12 +209,27 @@ class AssetModel extends SnipeModel
&& ($this->deleted_at == '');
}
/**
* Get uploads for this model
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany('\App\Models\Actionlog', 'item_id')
->where('item_type', '=', AssetModel::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
* Get user who created the item
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -235,10 +248,10 @@ class AssetModel extends SnipeModel
* scopeInCategory
* Get all models that are in the array of category ids
*
* @param $query
* @param $query
* @param array $categoryIdListing
*
* @return mixed
* @return mixed
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
*/
@@ -251,9 +264,9 @@ class AssetModel extends SnipeModel
* scopeRequestable
* Get all models that are requestable by a user.
*
* @param $query
* @param $query
*
* @return $query
* @return $query
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
* @version v3.5
*/
@@ -265,8 +278,8 @@ class AssetModel extends SnipeModel
/**
* Query builder scope to search on text, including catgeory and manufacturer name
*
* @param Illuminate\Database\Query\Builder $query Query builder instance
* @param text $search Search term
* @param Illuminate\Database\Query\Builder $query Query builder instance
* @param text $search Search term
*
* @return Illuminate\Database\Query\Builder Modified query builder
*/
@@ -274,31 +287,23 @@ class AssetModel extends SnipeModel
{
return $query->where('models.name', 'LIKE', "%$search%")
->orWhere('model_number', 'LIKE', "%$search%")
->orWhere(
function ($query) use ($search) {
$query->whereHas(
'category', function ($query) use ($search) {
$query->where('categories.name', 'LIKE', '%'.$search.'%');
}
);
}
)
->orWhere(
function ($query) use ($search) {
$query->whereHas(
'manufacturer', function ($query) use ($search) {
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
}
);
}
);
->orWhere(function ($query) use ($search) {
$query->whereHas('category', function ($query) use ($search) {
$query->where('categories.name', 'LIKE', '%'.$search.'%');
});
})
->orWhere(function ($query) use ($search) {
$query->whereHas('manufacturer', function ($query) use ($search) {
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
});
});
}
/**
* Query builder scope to order on manufacturer
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -310,8 +315,8 @@ class AssetModel extends SnipeModel
/**
* Query builder scope to order on category name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -327,6 +332,7 @@ class AssetModel extends SnipeModel
/**
* Query builder scope to order on created_by name
*
*/
public function scopeOrderByCreatedByName($query, $order)
{

View File

@@ -18,7 +18,7 @@ use Illuminate\Support\Str;
* to require acceptance from the user, whether or not to
* send a EULA to the user, etc.
*
* @version v1.0
* @version v1.0
*/
class Category extends SnipeModel
{
@@ -32,7 +32,6 @@ class Category extends SnipeModel
protected $hidden = ['created_by', 'deleted_at'];
protected $casts = [
'alert_on_response' => 'boolean',
'created_by' => 'integer',
];
@@ -70,7 +69,6 @@ class Category extends SnipeModel
'eula_text',
'name',
'require_acceptance',
'alert_on_response',
'use_default_eula',
'created_by',
'notes',
@@ -96,7 +94,7 @@ class Category extends SnipeModel
* Checks if category can be deleted
*
* @author [Dan Meltzer] [<dmeltzer.devel@gmail.com>]
* @since [v5.0]
* @since [v5.0]
* @return bool
*/
public function isDeletable()
@@ -119,7 +117,7 @@ class Category extends SnipeModel
* Establishes the category -> accessories relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessories()
@@ -131,7 +129,7 @@ class Category extends SnipeModel
* Establishes the category -> licenses relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.3]
* @since [v4.3]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -143,7 +141,7 @@ class Category extends SnipeModel
* Establishes the category -> consumables relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
@@ -155,7 +153,7 @@ class Category extends SnipeModel
* Establishes the category -> consumables relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
@@ -170,7 +168,7 @@ class Category extends SnipeModel
* It should only be used in a single category context.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return int
*/
public function itemCount()
@@ -181,18 +179,18 @@ class Category extends SnipeModel
}
switch ($this->category_type) {
case 'asset':
return $this->assets->count();
case 'accessory':
return $this->accessories->count();
case 'component':
return $this->components->count();
case 'consumable':
return $this->consumables->count();
case 'license':
return $this->licenses->count();
default:
return 0;
case 'asset':
return $this->assets->count();
case 'accessory':
return $this->accessories->count();
case 'component':
return $this->components->count();
case 'consumable':
return $this->consumables->count();
case 'license':
return $this->licenses->count();
default:
return 0;
}
}
@@ -201,7 +199,7 @@ class Category extends SnipeModel
* Establishes the category -> assets relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -218,8 +216,8 @@ class Category extends SnipeModel
* by their category.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.1.0]
* @see \App\Models\Asset::scopeAssetsForShow()
* @since [v6.1.0]
* @see \App\Models\Asset::scopeAssetsForShow()
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function showableAssets()
@@ -231,7 +229,7 @@ class Category extends SnipeModel
* Establishes the category -> models relationship
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function models()
@@ -249,7 +247,7 @@ class Category extends SnipeModel
* checks for a settings level EULA
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v2.0]
* @since [v2.0]
* @return string | null
*/
public function getEula()
@@ -276,7 +274,7 @@ class Category extends SnipeModel
*
* This will also correctly parse a 1/0 if "true"/"false" is passed.
*
* @param $value
* @param $value
* @return void
*/
public function setCheckinEmailAttribute($value)
@@ -293,9 +291,9 @@ class Category extends SnipeModel
/**
* Query builder scope for whether or not the category requires acceptance
*
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @author Vincent Sposato <vincent.sposato@gmail.com>
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeRequiresAcceptance($query)

View File

@@ -15,7 +15,6 @@ class CheckoutAcceptance extends Model
protected $casts = [
'accepted_at' => 'datetime',
'declined_at' => 'datetime',
'alert_on_response_id' => 'integer',
];
/**
@@ -66,7 +65,7 @@ class CheckoutAcceptance extends Model
/**
* Was the checkoutable checked out to this user?
*
* @param User $user
* @param User $user
* @return bool
*/
public function isCheckedOutTo(User $user)
@@ -79,7 +78,7 @@ class CheckoutAcceptance extends Model
* Do not add stuff here that doesn't have a corresponding column in the
* checkout_acceptances table or you'll get an error.
*
* @param string $signature_filename
* @param string $signature_filename
*/
public function accept($signature_filename, $eula = null, $filename = null, $note = null)
{
@@ -99,7 +98,7 @@ class CheckoutAcceptance extends Model
/**
* Decline the checkout acceptance
*
* @param string $signature_filename
* @param string $signature_filename
*/
public function decline($signature_filename, $note = null)
{
@@ -116,9 +115,8 @@ class CheckoutAcceptance extends Model
/**
* Filter checkout acceptences by the user
*
* @param Illuminate\Database\Eloquent\Builder $query
* @param User $user
* @param User $user
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForUser(Builder $query, User $user)
@@ -128,7 +126,6 @@ class CheckoutAcceptance extends Model
/**
* Filter to only get pending acceptances
*
* @param Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/

View File

@@ -13,13 +13,11 @@ use Illuminate\Support\Facades\Schema;
/**
* Model for Companies.
*
* @version v1.8
* @version v1.8
*/
final class Company extends SnipeModel
{
use HasFactory;
use CompanyableTrait;
protected $table = 'companies';
@@ -28,19 +26,19 @@ final class Company extends SnipeModel
'name' => 'required|min:1|max:255|unique:companies,name',
'fax' => 'min:7|max:35|nullable',
'phone' => 'min:7|max:35|nullable',
'email' => 'email|max:150|nullable',
'email' => 'email|max:150|nullable',
];
protected $presenter = \App\Presenters\CompanyPresenter::class;
use Presentable;
/**
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* Whether the model should inject it's identifier to the unique
* validation rules before attempting validation. If this property
* is not set in the model it will default to true.
*
* @var bool
*/
*/
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
use Searchable;
@@ -102,7 +100,7 @@ final class Company extends SnipeModel
* account the full multiple company support setting
* and if the current user is a super user.
*
* @param $unescaped_input
* @param $unescaped_input
* @return int|mixed|string|null
*/
public static function getIdForCurrentUser($unescaped_input)
@@ -129,7 +127,7 @@ final class Company extends SnipeModel
* Check to see if the current user should have access to the model.
* I hate this method and I think it should be refactored.
*
* @param $companyable
* @param $companyable
* @return bool|void
*/
public static function isCurrentUserHasAccess($companyable)
@@ -148,10 +146,10 @@ final class Company extends SnipeModel
if (!is_string($companyable)) {
$company_table = $companyable->getModel()->getTable();
try {
// This is primarily for the gate:allows-check in location->isDeletable()
// This is primary for the gate:allows-check in location->isDeletable()
// Locations don't have a company_id so without this it isn't possible to delete locations with FullMultipleCompanySupport enabled
// because this function is called by SnipePermissionsPolicy->before()
if (!Schema::hasColumn($company_table, 'company_id')) {
if (!$companyable instanceof Company && !Schema::hasColumn($company_table, 'company_id')) {
return true;
}
@@ -162,19 +160,12 @@ final class Company extends SnipeModel
if (auth()->user()) {
// Log::warning('Companyable is '.$companyable);
Log::warning('Companyable is '.$companyable);
$current_user_company_id = auth()->user()->company_id;
$companyable_company_id = $companyable->company_id;
// Set this to check companyable on company
if ($companyable instanceof Company) {
$companyable_company_id = $companyable->id;
}
return ($current_user_company_id == null) || ($current_user_company_id == $companyable_company_id) || auth()->user()->isSuperUser();
return $current_user_company_id == null || $current_user_company_id == $companyable_company_id || auth()->user()->isSuperUser();
}
return false;
}
public static function isCurrentUserAuthorized()
@@ -192,7 +183,7 @@ final class Company extends SnipeModel
* Checks if company can be deleted
*
* @author [Dan Meltzer] [<dmeltzer.devel@gmail.com>]
* @since [v5.0]
* @since [v5.0]
* @return bool
*/
public function isDeletable()
@@ -209,7 +200,7 @@ final class Company extends SnipeModel
}
/**
* @param $unescaped_input
* @param $unescaped_input
* @return int|mixed|string|null
*/
public static function getIdForUser($unescaped_input)
@@ -265,14 +256,14 @@ final class Company extends SnipeModel
* @todo - refactor that trait to handle the user's model as well.
*
* @author [A. Gianotto] <snipe@snipe.net>
* @param $query
* @param $column
* @param $table_name
* @param $query
* @param $column
* @param $table_name
* @return mixed
*/
public static function scopeCompanyables($query, $column = 'company_id', $table_name = null)
{
// If not logged in and hitting this, assume we are on the command line and don't scope?
// If not logged in and hitting this, assume we are on the command line and don't scope?'
if (! static::isFullMultipleCompanySupportEnabled() || (Auth::hasUser() && auth()->user()->isSuperUser()) || (! Auth::hasUser())) {
return $query;
} else {
@@ -289,16 +280,11 @@ final class Company extends SnipeModel
private static function scopeCompanyablesDirectly($query, $column = 'company_id', $table_name = null)
{
$company_id = null;
// Get the company ID of the logged-in user, or set it to null if there is no company associated with the user
if (Auth::hasUser()) {
$company_id = auth()->user()->company_id;
}
// If we are scoping the companies table itself, look for the company.id
if ($query->getModel()->getTable() == 'companies') {
return $query->where('companies.id', '=', $company_id);
} else {
$company_id = null;
}
@@ -311,8 +297,6 @@ final class Company extends SnipeModel
return $query->where($table.$column, '=', $company_id);
}
}
public function adminuser()
@@ -327,8 +311,8 @@ final class Company extends SnipeModel
* This gets invoked by CompanyableChildScope, but I'm not sure what it does.
*
* @author [A. Gianotto] <snipe@snipe.net>
* @param array $companyable_names
* @param $query
* @param array $companyable_names
* @param $query
* @return mixed
*/
public static function scopeCompanyableChildren(array $companyable_names, $query)
@@ -340,18 +324,17 @@ final class Company extends SnipeModel
return $query;
} else {
$f = function ($q) {
Log::debug('scopeCompanyablesDirectly firing ');
static::scopeCompanyablesDirectly($q);
};
$q = $query->where(
function ($q) use ($companyable_names, $f) {
$q2 = $q->whereHas($companyable_names[0], $f);
$q = $query->where(function ($q) use ($companyable_names, $f) {
$q2 = $q->whereHas($companyable_names[0], $f);
for ($i = 1; $i < count($companyable_names); $i++) {
$q2 = $q2->orWhereHas($companyable_names[$i], $f);
}
for ($i = 1; $i < count($companyable_names); $i++) {
$q2 = $q2->orWhereHas($companyable_names[$i], $f);
}
);
});
return $q;
}

View File

@@ -9,15 +9,15 @@ use Illuminate\Database\Eloquent\Scope;
/**
* Handle query scoping for full company support.
*
* @todo Move this to a more Laravel 5.2 esque way
* @version v1.0
* @todo Move this to a more Laravel 5.2 esque way
* @version v1.0
*/
final class CompanyableChildScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function apply(Builder $builder, Model $model)
@@ -31,7 +31,7 @@ final class CompanyableChildScope implements Scope
* @todo IMPLEMENT
* Remove the scope from the given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function remove(Builder $builder)

View File

@@ -9,15 +9,15 @@ use Illuminate\Database\Eloquent\Scope;
/**
* Handle query scoping for full company support.
*
* @todo Move this to a more Laravel 5.2 esque way
* @version v1.0
* @todo Move this to a more Laravel 5.2 esque way
* @version v1.0
*/
final class CompanyableScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function apply(Builder $builder, Model $model)
@@ -29,7 +29,7 @@ final class CompanyableScope implements Scope
* @todo IMPLEMENT
* Remove the scope from the given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function remove(Builder $builder)

View File

@@ -8,7 +8,7 @@ trait CompanyableTrait
* This trait is used to scope models to the current company. To use this scope on companyable models,
* we use the "use Companyable;" statement at the top of the mode.
*
* @see \App\Models\Company\Company::scopeCompanyables()
* @see \App\Models\Company\Company::scopeCompanyables()
* @return void
*/
public static function bootCompanyableTrait()

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