diff --git a/.all-contributorsrc b/.all-contributorsrc
index ba9f54a99b..b91e5afeb0 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -4180,6 +4180,24 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "lukaskraic",
+ "name": "Lukas Kraic",
+ "avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
+ "profile": "https://github.com/lukaskraic",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "mckaygerhard",
+ "name": "Герхард PICCORO Lenz McKAY ",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1571724?v=4",
+ "profile": "https://github-readme-stats.vercel.app/api?username=mckaygerhard",
+ "contributions": [
+ "code"
+ ]
}
]
}
diff --git a/.env.docker b/.env.docker
index 88b65b8f79..9eae34385e 100644
--- a/.env.docker
+++ b/.env.docker
@@ -28,6 +28,7 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=db
+DB_SOCKET=null
DB_PORT='3306'
DB_DATABASE=snipeit
DB_USERNAME=snipeit
@@ -168,6 +169,7 @@ AWS_DEFAULT_REGION=null
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
+INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC
diff --git a/.env.example b/.env.example
index 81de24ead1..61ec3295d1 100644
--- a/.env.example
+++ b/.env.example
@@ -24,6 +24,7 @@ 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
@@ -174,6 +175,7 @@ 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
diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml
index a11509d69e..32de700bdc 100644
--- a/.github/workflows/codacy-analysis.yml
+++ b/.github/workflows/codacy-analysis.yml
@@ -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.5
+ uses: codacy/codacy-analysis-cli-action@v4.4.7
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
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 7ab3810780..ed141050d5 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -67,7 +67,8 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [
AlexanderWPapyrus](https://github.com/AlexanderWPapyrus)
[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [
Alexandr Hacicheant](https://github.com/disc)
[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [
Hex](https://hex128.io/)
[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [
Arunas Skirius](https://github.com/arukompas)
[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [
Ben Periton](https://github.com/benperiton)
[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [
Byron Wolfman](https://wolfman.dev/)
[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [
Calvin](https://github.com/CalvinSchwartz)
[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [
Juan Font](https://github.com/juanfont)
[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [
Juho Taipale](https://github.com/juhotaipale)
[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [
Korvin Szanto](https://github.com/KorvinSzanto)
[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [
Lewis Foster](https://lewisfoster.foo/)
[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [
Logan Swartzendruber](https://github.com/loganswartz)
[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [
Lorenzo P.](https://github.com/lopezio)
[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [
Lukas Jung](https://github.com/m4us1ne)
[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [
Ellie](https://leafedfox.xyz/)
[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [
GA Stamper](https://github.com/gastamper)
[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [
Guillaume Lefranc](https://github.com/gl-pup)
[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [
Hajo Möller](https://github.com/dasjoe)
[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [
Istvan Basa](https://github.com/pottom)
[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [
JJ Asghar](https://jjasghar.github.io/)
[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [
James E. Msenga](https://github.com/JemCdo)
[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
-| [
Jan Felix Wiebe](https://github.com/jfwiebe)
[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [
Jo Drexl](https://www.nfon.com/)
[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [
Austin Sasko](https://github.com/austinsasko)
[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [
Jasson](http://jassoncordones.github.io)
[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [
Okean](https://github.com/Tinyblargon)
[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [
Alejandro Medrano](https://www.lst.tfo.upm.es/alejandro-medrano/)
[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") |
+| [
Jan Felix Wiebe](https://github.com/jfwiebe)
[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [
Jo Drexl](https://www.nfon.com/)
[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [
Austin Sasko](https://github.com/austinsasko)
[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [
Jasson](http://jassoncordones.github.io)
[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [
Okean](https://github.com/Tinyblargon)
[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [
Alejandro Medrano](https://www.lst.tfo.upm.es/alejandro-medrano/)
[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [
Lukas Kraic](https://github.com/lukaskraic)
[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
+| [
Герхард PICCORO Lenz McKAY ](https://github-readme-stats.vercel.app/api?username=mckaygerhard)
[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") |
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
diff --git a/app/Console/Commands/CleanIncorrectCheckoutAcceptances.php b/app/Console/Commands/CleanIncorrectCheckoutAcceptances.php
new file mode 100644
index 0000000000..30dca964c7
--- /dev/null
+++ b/app/Console/Commands/CleanIncorrectCheckoutAcceptances.php
@@ -0,0 +1,68 @@
+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");
+ }
+}
diff --git a/app/Console/Commands/CleanOldCheckoutRequests.php b/app/Console/Commands/CleanOldCheckoutRequests.php
new file mode 100644
index 0000000000..a96a58a349
--- /dev/null
+++ b/app/Console/Commands/CleanOldCheckoutRequests.php
@@ -0,0 +1,74 @@
+ function ($query) {
+ $query->withTrashed();
+ },
+ 'requestedItem' => function ($query) {
+ $query->withTrashed();
+ },
+ ])->get();
+
+ $this->info("Processing {$requests->count()} checkout requests");
+
+ $this->withProgressBar($requests, function ($request) {
+ if ($this->shouldForceDelete($request)) {
+ $request->forceDelete();
+ $this->deletions++;
+ return;
+ }
+
+ if ($this->shouldSoftDelete($request)) {
+ $request->delete();
+ $this->deletions++;
+ return;
+ }
+
+ $this->skips++;
+ });
+
+ $this->info("Final deletion count: $this->deletions, and skip count: $this->skips");
+
+ return 0;
+ }
+
+ private function shouldForceDelete(CheckoutRequest $request)
+ {
+ // check if the requestable or user relationship is null
+ return !$request->requestable || !$request->user;
+ }
+
+ private function shouldSoftDelete(CheckoutRequest $request)
+ {
+ return $request->requestable->trashed() || $request->user->trashed();
+ }
+}
diff --git a/app/Console/Commands/LdapSync.php b/app/Console/Commands/LdapSync.php
index f90aeafd8d..88015b14a1 100644
--- a/app/Console/Commands/LdapSync.php
+++ b/app/Console/Commands/LdapSync.php
@@ -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'] = 0;
+ $results[$i]['location_id'] = null;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
diff --git a/app/Console/Commands/MoveUploadsToNewDisk.php b/app/Console/Commands/MoveUploadsToNewDisk.php
index a27b06feed..615c12a545 100644
--- a/app/Console/Commands/MoveUploadsToNewDisk.php
+++ b/app/Console/Commands/MoveUploadsToNewDisk.php
@@ -96,7 +96,7 @@ class MoveUploadsToNewDisk extends Command
$private_uploads['assets'] = glob('storage/private_uploads/assets'."/*.*");
$private_uploads['signatures'] = glob('storage/private_uploads/signatures'."/*.*");
$private_uploads['audits'] = glob('storage/private_uploads/audits'."/*.*");
- $private_uploads['assetmodels'] = glob('storage/private_uploads/assetmodels'."/*.*");
+ $private_uploads['assetmodels'] = glob('storage/private_uploads/models'."/*.*");
$private_uploads['imports'] = glob('storage/private_uploads/imports'."/*.*");
$private_uploads['licenses'] = glob('storage/private_uploads/licenses'."/*.*");
$private_uploads['users'] = glob('storage/private_uploads/users'."/*.*");
diff --git a/app/Console/Commands/PaveIt.php b/app/Console/Commands/PaveIt.php
index ef69d25a5b..36aacd6636 100644
--- a/app/Console/Commands/PaveIt.php
+++ b/app/Console/Commands/PaveIt.php
@@ -4,7 +4,7 @@ namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\CustomField;
-use Schema;
+use Illuminate\Support\Facades\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 id > 2');
+ DB::statement('delete from oauth_clients WHERE id > 2');
+ DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
}
}
\ No newline at end of file
diff --git a/app/Console/Commands/Purge.php b/app/Console/Commands/Purge.php
index 1dd2aaa51d..351bacf715 100644
--- a/app/Console/Commands/Purge.php
+++ b/app/Console/Commands/Purge.php
@@ -62,19 +62,19 @@ class Purge extends Command
$assetcount = $assets->count();
$this->info($assets->count().' assets purged.');
$asset_assoc = 0;
- $asset_maintenances = 0;
+ $maintenances = 0;
foreach ($assets as $asset) {
$this->info('- Asset "'.$asset->present()->name().'" deleted.');
$asset_assoc += $asset->assetlog()->count();
$asset->assetlog()->forceDelete();
- $asset_maintenances += $asset->assetmaintenances()->count();
- $asset->assetmaintenances()->forceDelete();
+ $maintenances += $asset->maintenances()->count();
+ $asset->maintenances()->forceDelete();
$asset->forceDelete();
}
$this->info($asset_assoc.' corresponding log records purged.');
- $this->info($asset_maintenances.' corresponding maintenance records purged.');
+ $this->info($maintenances.' corresponding maintenance records purged.');
$locations = Location::whereNotNull('deleted_at')->withTrashed()->get();
$this->info($locations->count().' locations purged.');
diff --git a/app/Console/Commands/RestoreFromBackup.php b/app/Console/Commands/RestoreFromBackup.php
index 35e67a82e7..b5109c25ec 100644
--- a/app/Console/Commands/RestoreFromBackup.php
+++ b/app/Console/Commands/RestoreFromBackup.php
@@ -243,6 +243,8 @@ class RestoreFromBackup extends Command
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels',
+ 'storage/private_uploads/maintenances',
+ 'storage/private_uploads/models',
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
@@ -260,9 +262,10 @@ class RestoreFromBackup extends Command
];
$public_dirs = [
'public/uploads/accessories',
+ 'public/uploads/assetmodels',
+ 'public/uploads/maintenances',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
- //'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
'public/uploads/categories',
'public/uploads/companies',
'public/uploads/components',
@@ -329,9 +332,9 @@ class RestoreFromBackup extends Command
}
}
}
- $good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
- 'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico', 'avif'
- ];
+
+ $good_extensions = config('filesystems.allowed_upload_extensions_array');
+
foreach (array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index e6cc058fce..83411030d1 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -116,17 +116,34 @@ class Handler extends ExceptionHandler
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() : [];
- // This gets the MVC model name from the exception and formats in a way that's less fugly
+ 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
$model_name = strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel())))));
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
@@ -145,6 +162,8 @@ class Handler extends ExceptionHandler
$route = 'licenses.index';
} elseif ($route === 'customfields.index') {
$route = 'fields.index';
+ } elseif ($route === 'customfieldsets.index') {
+ $route = 'fields.index';
}
return redirect()
diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php
index f544f2bc36..33fc21fc9d 100644
--- a/app/Helpers/Helper.php
+++ b/app/Helpers/Helper.php
@@ -13,6 +13,7 @@ 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;
@@ -122,9 +123,9 @@ class Helper
if (is_numeric($cost)) {
if (Setting::getSettings()->digit_separator=='1.234,56') {
- return number_format($cost, 2, ',', '.');
+ return (float) number_format($cost, 2, ',', '.');
}
- return number_format($cost, 2, '.', ',');
+ return (float) number_format($cost, 2, '.', ',');
}
// It's already been parsed.
return $cost;
@@ -876,6 +877,48 @@ 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] []
+ * @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] []
+ * @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.
@@ -1170,6 +1213,15 @@ 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)) {
@@ -1480,68 +1532,59 @@ class Helper
}
- static public function getRedirectOption($request, $id, $table, $item_id = null)
+ static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
{
- $redirect_option = Session::get('redirect_option');
- $checkout_to_type = Session::get('checkout_to_type');
+ $redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
+ $checkout_to_type = Session::get('checkout_to_type') ?? null;
$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') {
+ return redirect()->to($backUrl);
+ }
// return to index
if ($redirect_option == '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 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'),
+ };
}
// return to thing being assigned
if ($redirect_option == 'item') {
- 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 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),
+ };
}
// return to assignment target
if ($redirect_option == 'target') {
- 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 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),
+ };
}
// return to somewhere else
if ($redirect_option == 'other_redirect') {
- switch ($other_redirect) {
- case 'audit':
- return route('assets.audit.due');
- }
+ return match ($other_redirect) {
+ 'audit' => redirect()->route('assets.audit.due'),
+ 'model' => redirect()->route('models.show', $request->model_id),
+ };
}
diff --git a/app/Helpers/IconHelper.php b/app/Helpers/IconHelper.php
index 7c8e2a7456..668b01a9f6 100644
--- a/app/Helpers/IconHelper.php
+++ b/app/Helpers/IconHelper.php
@@ -43,6 +43,8 @@ class IconHelper
return 'fa-regular fa-envelope';
case 'phone':
return 'fa-solid fa-phone';
+ case 'mobile':
+ return 'fas fa-mobile-screen-button';
case 'long-arrow-right':
return 'fas fa-long-arrow-alt-right';
case 'download':
@@ -151,6 +153,7 @@ 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';
diff --git a/app/Helpers/StorageHelper.php b/app/Helpers/StorageHelper.php
index 47700f913a..1b55b69a3f 100644
--- a/app/Helpers/StorageHelper.php
+++ b/app/Helpers/StorageHelper.php
@@ -16,38 +16,75 @@ 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 [
- * @since v7.0.14
- * @param $file_with_path
+ * @since v7.0.14
+ * @param $file_with_path
* @return bool
*/
- public static function allowSafeInline($file_with_path) {
+ public static function allowSafeInline($file_with_path)
+ {
$allowed_inline = [
- 'pdf',
- 'svg',
- 'jpg',
- 'gif',
- 'svg',
'avif',
- 'webp',
+ 'gif',
+ 'gif',
+ 'jpg',
+ 'mov',
+ 'mp3',
+ 'mp4',
+ 'ogg',
+ 'pdf',
'png',
+ 'svg',
+ 'wav',
+ 'webm',
+ 'webp',
];
@@ -59,10 +96,24 @@ 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 = [];
diff --git a/app/Http/Controllers/Accessories/AccessoriesController.php b/app/Http/Controllers/Accessories/AccessoriesController.php
index 66a914e9a9..00cfce0516 100755
--- a/app/Http/Controllers/Accessories/AccessoriesController.php
+++ b/app/Http/Controllers/Accessories/AccessoriesController.php
@@ -77,13 +77,30 @@ class AccessoriesController extends Controller
$accessory->supplier_id = request('supplier_id');
$accessory->notes = request('notes');
- $accessory = $request->handleImages($accessory);
+ if ($request->has('use_cloned_image')) {
+ $cloned_model_img = Accessory::select('image')->find($request->input('clone_image_from_id'));
+ if ($cloned_model_img) {
+ $new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
+ $new_image = 'accessories/'.$new_image_name;
+ Storage::disk('public')->copy('accessories/'.$cloned_model_img->image, $new_image);
+ $accessory->image = $new_image_name;
+ }
+
+ } else {
+ $accessory = $request->handleImages($accessory);
+ }
+
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
- session()->put(['redirect_option' => $request->get('redirect_option')]);
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
- return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
+ return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
+ ->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -113,11 +130,12 @@ 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);
}
@@ -167,7 +185,8 @@ class AccessoriesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
+ return 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'));
diff --git a/app/Http/Controllers/Accessories/AccessoriesFilesController.php b/app/Http/Controllers/Accessories/AccessoriesFilesController.php
deleted file mode 100644
index 9dbb16d83a..0000000000
--- a/app/Http/Controllers/Accessories/AccessoriesFilesController.php
+++ /dev/null
@@ -1,132 +0,0 @@
-]
- * @since [v1.0]
- * @todo Switch to using the AssetFileRequest form request validator.
- */
- public function store(UploadFileRequest $request, $accessoryId = null) : RedirectResponse
- {
-
- if (config('app.lock_passwords')) {
- return redirect()->route('accessories.show', ['accessory'=>$accessoryId])->with('error', trans('general.feature_disabled'));
- }
-
- $accessory = Accessory::find($accessoryId);
-
- if (isset($accessory->id)) {
- $this->authorize('accessories.files', $accessory);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/accessories')) {
- Storage::makeDirectory('private_uploads/accessories', 775);
- }
-
- foreach ($request->file('file') as $file) {
-
- $file_name = $request->handleFile('private_uploads/accessories/', 'accessory-'.$accessory->id, $file);
- //Log the upload to the log
- $accessory->logUpload($file_name, e($request->input('notes')));
- }
-
-
- return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
-
- }
-
- return redirect()->route('accessories.show', $accessory->id)->withFragment('files')->with('error', trans('general.no_files_uploaded'));
- }
- // Prepare the error message
- return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
-
- }
-
- /**
- * Deletes the selected accessory file.
- *
- * @author [A. Gianotto] []
- * @since [v1.0]
- * @param int $accessoryId
- * @param int $fileId
- */
- public function destroy($accessoryId = null, $fileId = null) : RedirectResponse
- {
- if ($accessory = Accessory::find($accessoryId)) {
- $this->authorize('update', $accessory);
-
- if ($log = Actionlog::find($fileId)) {
-
- if (Storage::exists('private_uploads/accessories/'.$log->filename)) {
- try {
- Storage::delete('private_uploads/accessories/' . $log->filename);
- $log->delete();
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
- } catch (\Exception $e) {
- Log::debug($e);
- return redirect()->route('accessories.index')->with('error', trans('general.file_does_not_exist'));
- }
- }
-
- }
- return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
- }
-
- return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
- }
-
- /**
- * Allows the selected file to be viewed.
- *
- * @author [A. Gianotto] []
- * @since [v1.4]
- * @param int $accessoryId
- * @param int $fileId
- */
- public function show($accessoryId = null, $fileId = null) : View | RedirectResponse | Response | BinaryFileResponse | StreamedResponse
- {
-
-
- // the accessory is valid
- if ($accessory = Accessory::find($accessoryId)) {
- $this->authorize('view', $accessory);
- $this->authorize('accessories.files', $accessory);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $accessory->id)->find($fileId)) {
- $file = 'private_uploads/accessories/'.$log->filename;
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('accessories.show', ['accessory' => $accessory])->with('error', trans('general.file_not_found'));
- }
- }
-
- return redirect()->route('accessories.show', ['accessory' => $accessory])->withFragment('files')->with('error', trans('general.log_record_not_found'));
-
- }
-
- return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
-
- }
-}
diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php
index ab24200d78..a7655a278b 100644
--- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php
+++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php
@@ -78,7 +78,8 @@ class AccessoryCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
- return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
+ return 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'));
diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php
index 58ce787245..05d60fcbfe 100644
--- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php
+++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php
@@ -97,7 +97,7 @@ class AccessoryCheckoutController extends Controller
// Redirect to the new accessory page
- return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
+ return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkout.success'));
}
}
diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php
index b20f7d97e8..c273d36e3a 100644
--- a/app/Http/Controllers/Account/AcceptanceController.php
+++ b/app/Http/Controllers/Account/AcceptanceController.php
@@ -7,6 +7,7 @@ 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;
@@ -20,9 +21,12 @@ 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;
@@ -148,6 +152,8 @@ class AcceptanceController extends Controller
}
}
+
+ $assigned_user = User::find($acceptance->assigned_to_id);
// this is horrible
switch($acceptance->checkoutable_type){
case 'App\Models\Asset':
@@ -157,35 +163,30 @@ 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') {
@@ -226,7 +227,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_to,
+ 'assigned_to' => $assigned_user->present()->fullName,
'company_name' => $branding_settings->site_name,
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => $path_logo,
@@ -240,6 +241,19 @@ 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) {
@@ -333,10 +347,29 @@ class AcceptanceController extends Controller
$acceptance->decline($sig_filename, $request->input('note'));
$acceptance->notify(new AcceptanceAssetDeclinedNotification($data));
+ Log::debug('New event acceptance.');
event(new CheckoutDeclined($acceptance));
$return_msg = trans('admin/users/message.declined');
}
+ if ($acceptance->alert_on_response_id) {
+ try {
+ $recipient = User::find($acceptance->alert_on_response_id);
+
+ if ($recipient) {
+ Log::debug('Attempting to send email acceptance.');
+ Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
+ $acceptance,
+ $recipient,
+ $request->input('asset_acceptance') === 'accepted',
+ ));
+ Log::debug('Send email notification sucess on checkout acceptance response.');
+ }
+ } catch (Exception $e) {
+ Log::error($e->getMessage());
+ Log::warning($e);
+ }
+ }
return redirect()->to('account/accept')->with('success', $return_msg);
diff --git a/app/Http/Controllers/Api/AssetFilesController.php b/app/Http/Controllers/Api/AssetFilesController.php
deleted file mode 100644
index fabe9ebbb3..0000000000
--- a/app/Http/Controllers/Api/AssetFilesController.php
+++ /dev/null
@@ -1,200 +0,0 @@
-
- *
- * @version v1.0
- * @author [T. Scarsbrook] []
- */
-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] []
- */
- 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] []
- */
- 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] []
- */
- 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] []
- */
- 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);
- }
-}
diff --git a/app/Http/Controllers/Api/AssetModelFilesController.php b/app/Http/Controllers/Api/AssetModelFilesController.php
deleted file mode 100644
index d0a4747bf4..0000000000
--- a/app/Http/Controllers/Api/AssetModelFilesController.php
+++ /dev/null
@@ -1,184 +0,0 @@
-
- *
- * @version v1.0
- * @author [T. Scarsbrook] []
- */
-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);
- }
-}
diff --git a/app/Http/Controllers/Api/AssetModelsController.php b/app/Http/Controllers/Api/AssetModelsController.php
index 3b6b287334..1ed327b3bf 100644
--- a/app/Http/Controllers/Api/AssetModelsController.php
+++ b/app/Http/Controllers/Api/AssetModelsController.php
@@ -154,7 +154,7 @@ class AssetModelsController extends Controller
$assetmodel = $request->handleImages($assetmodel);
if ($assetmodel->save()) {
- return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
+ return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
@@ -207,7 +207,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 +222,7 @@ class AssetModelsController extends Controller
if ($assetmodel->save()) {
- return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
+ return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php
index 57f188d92f..160f4a120a 100644
--- a/app/Http/Controllers/Api/AssetsController.php
+++ b/app/Http/Controllers/Api/AssetsController.php
@@ -114,6 +114,7 @@ class AssetsController extends Controller
'byod',
'asset_eol_date',
'requestable',
+ 'jobtitle',
];
$filter = [];
@@ -395,6 +396,9 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
+ case 'jobtitle':
+ $assets->OrderByJobTitle($order);
+ break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;
diff --git a/app/Http/Controllers/Api/CompaniesController.php b/app/Http/Controllers/Api/CompaniesController.php
index fd7f57ddce..aee38301f4 100644
--- a/app/Http/Controllers/Api/CompaniesController.php
+++ b/app/Http/Controllers/Api/CompaniesController.php
@@ -43,7 +43,10 @@ class CompaniesController extends Controller
$companies = Company::withCount(['assets as assets_count' => function ($query) {
$query->AssetsForShow();
- }])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
+ }])
+ ->with('adminuser')
+ ->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'));
@@ -119,6 +122,7 @@ class CompaniesController extends Controller
{
$this->authorize('view', Company::class);
$company = Company::findOrFail($id);
+ $this->authorize('view', $company);
return (new CompaniesTransformer)->transformCompany($company);
}
@@ -136,6 +140,7 @@ 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);
@@ -188,6 +193,7 @@ class CompaniesController extends Controller
'companies.image',
]);
+
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}
diff --git a/app/Http/Controllers/Api/ConsumablesController.php b/app/Http/Controllers/Api/ConsumablesController.php
index 7bddde070c..35fb08470a 100644
--- a/app/Http/Controllers/Api/ConsumablesController.php
+++ b/app/Http/Controllers/Api/ConsumablesController.php
@@ -228,11 +228,16 @@ class ConsumablesController extends Controller
foreach ($consumable->consumableAssignments as $consumable_assignment) {
$rows[] = [
'avatar' => ($consumable_assignment->user) ? e($consumable_assignment->user->present()->gravatar) : '',
- 'name' => ($consumable_assignment->user) ? $consumable_assignment->user->present()->nameUrl() : 'Deleted User',
+ 'user' => ($consumable_assignment->user) ? [
+ 'id' => (int) $consumable_assignment->user->id,
+ 'name'=> e($consumable_assignment->user->present()->fullName()),
+ ] : null,
'created_at' => Helper::getFormattedDateObject($consumable_assignment->created_at, 'datetime'),
'note' => ($consumable_assignment->note) ? e($consumable_assignment->note) : null,
- 'admin' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null, // legacy, so we don't change the shape of the response
- 'created_by' => ($consumable_assignment->adminuser) ? $consumable_assignment->adminuser->present()->nameUrl() : null,
+ 'created_by' => ($consumable_assignment->adminuser) ? [
+ 'id' => (int) $consumable_assignment->adminuser->id,
+ 'name'=> e($consumable_assignment->adminuser->present()->fullName()),
+ ] : null,
];
}
diff --git a/app/Http/Controllers/Api/LicenseSeatsController.php b/app/Http/Controllers/Api/LicenseSeatsController.php
index babca4a319..934261e97a 100644
--- a/app/Http/Controllers/Api/LicenseSeatsController.php
+++ b/app/Http/Controllers/Api/LicenseSeatsController.php
@@ -29,6 +29,15 @@ 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') {
diff --git a/app/Http/Controllers/Api/AssetMaintenancesController.php b/app/Http/Controllers/Api/MaintenancesController.php
similarity index 75%
rename from app/Http/Controllers/Api/AssetMaintenancesController.php
rename to app/Http/Controllers/Api/MaintenancesController.php
index b4e9b44196..86f561c86c 100644
--- a/app/Http/Controllers/Api/AssetMaintenancesController.php
+++ b/app/Http/Controllers/Api/MaintenancesController.php
@@ -4,11 +4,11 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
-use App\Http\Transformers\AssetMaintenancesTransformer;
+use App\Http\Requests\ImageUploadRequest;
+use App\Http\Transformers\MaintenancesTransformer;
use App\Models\Asset;
-use App\Models\AssetMaintenance;
+use App\Models\Maintenance;
use App\Models\Company;
-use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
@@ -18,13 +18,13 @@ use Illuminate\Http\JsonResponse;
*
* @version v2.0
*/
-class AssetMaintenancesController extends Controller
+class MaintenancesController extends Controller
{
/**
* Generates the JSON response for asset maintenances listing view.
*
- * @see AssetMaintenancesController::getIndex() method that generates view
+ * @see MaintenancesController::getIndex() method that generates view
* @author Vincent Sposato
* @version v1.0
* @since [v1.8]
@@ -33,7 +33,7 @@ class AssetMaintenancesController extends Controller
{
$this->authorize('view', Asset::class);
- $maintenances = AssetMaintenance::select('asset_maintenances.*')
+ $maintenances = Maintenance::select('maintenances.*')
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'adminuser');
if ($request->filled('search')) {
@@ -45,11 +45,11 @@ class AssetMaintenancesController extends Controller
}
if ($request->filled('supplier_id')) {
- $maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
+ $maintenances->where('maintenances.supplier_id', '=', $request->input('supplier_id'));
}
if ($request->filled('created_by')) {
- $maintenances->where('asset_maintenances.created_by', '=', $request->input('created_by'));
+ $maintenances->where('maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('asset_maintenance_type')) {
@@ -63,7 +63,7 @@ class AssetMaintenancesController extends Controller
$allowed_columns = [
'id',
- 'title',
+ 'name',
'asset_maintenance_time',
'asset_maintenance_type',
'cost',
@@ -75,6 +75,7 @@ class AssetMaintenancesController extends Controller
'serial',
'created_by',
'supplier',
+ 'location',
'is_warranty',
'status_label',
];
@@ -98,6 +99,9 @@ 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;
@@ -108,7 +112,7 @@ class AssetMaintenancesController extends Controller
$total = $maintenances->count();
$maintenances = $maintenances->skip($offset)->take($limit)->get();
- return (new AssetMaintenancesTransformer())->transformAssetMaintenances($maintenances, $total);
+ return (new MaintenancesTransformer())->transformMaintenances($maintenances, $total);
}
@@ -117,22 +121,23 @@ class AssetMaintenancesController extends Controller
/**
* Validates and stores the new asset maintenance
*
- * @see AssetMaintenancesController::getCreate() method for the form
+ * @see MaintenancesController::getCreate() method for the form
* @author Vincent Sposato
* @version v1.0
* @since [v1.8]
*/
- public function store(Request $request) : JsonResponse | array
+ public function store(ImageUploadRequest $request) : JsonResponse | array
{
$this->authorize('update', Asset::class);
+
// create a new model instance
- $maintenance = new AssetMaintenance();
+ $maintenance = new Maintenance();
$maintenance->fill($request->all());
$maintenance->created_by = auth()->id();
-
+ $maintenance = $request->handleImages($maintenance);
// Was the asset maintenance created?
if ($maintenance->save()) {
- return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
+ return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.create.success')));
}
@@ -153,11 +158,11 @@ class AssetMaintenancesController extends Controller
{
$this->authorize('update', Asset::class);
- if ($maintenance = AssetMaintenance::with('asset')->find($id)) {
+ if ($maintenance = Maintenance::with('asset')->find($id)) {
// Can this user manage this asset?
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
- return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
+ return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.action_permission_denied', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id, 'action' => trans('general.edit')])));
}
// The asset this miantenance is attached to is not valid or has been deleted
@@ -168,13 +173,13 @@ class AssetMaintenancesController extends Controller
$maintenance->fill($request->all());
if ($maintenance->save()) {
- return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
+ return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.edit.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
}
- return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/asset_maintenances/general.maintenance'), 'id' => $id])));
+ return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('admin/maintenances/general.maintenance'), 'id' => $id])));
}
@@ -182,20 +187,20 @@ class AssetMaintenancesController extends Controller
* Delete an asset maintenance
*
* @author A. Gianotto
- * @param int $assetMaintenanceId
+ * @param int $maintenanceId
* @version v1.0
* @since [v4.0]
*/
- public function destroy($assetMaintenanceId) : JsonResponse | array
+ public function destroy($maintenanceId) : JsonResponse | array
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
- $assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
+ $maintenance = Maintenance::findOrFail($maintenanceId);
- $assetMaintenance->delete();
+ $maintenance->delete();
- return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.delete.success')));
+ return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/maintenances/message.delete.success')));
}
@@ -204,19 +209,19 @@ class AssetMaintenancesController extends Controller
* View an asset maintenance
*
* @author A. Gianotto
- * @param int $assetMaintenanceId
+ * @param int $maintenanceId
* @version v1.0
* @since [v4.0]
*/
- public function show($assetMaintenanceId) : JsonResponse | array
+ public function show($maintenanceId) : JsonResponse | array
{
$this->authorize('view', Asset::class);
- $assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
- if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
+ $maintenance = Maintenance::findOrFail($maintenanceId);
+ if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot view a maintenance for that asset'));
}
- return (new AssetMaintenancesTransformer())->transformAssetMaintenance($assetMaintenance);
+ return (new MaintenancesTransformer())->transformMaintenance($maintenance);
}
}
diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php
index f7f91e094e..69db8aae04 100644
--- a/app/Http/Controllers/Api/ProfileController.php
+++ b/app/Http/Controllers/Api/ProfileController.php
@@ -4,15 +4,19 @@ 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
{
@@ -167,6 +171,22 @@ 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] []
+ */
+ public function eulas(ProfileTransformer $transformer)
+ {
+ // Only return this user's EULAs
+ $eulas = auth()->user()->eulas;
+ return response()->json(
+ $transformer->transformFiles($eulas, $eulas->count())
+ );
+ }
}
diff --git a/app/Http/Controllers/Api/SettingsController.php b/app/Http/Controllers/Api/SettingsController.php
index 7eb28a4815..134d24ef2a 100644
--- a/app/Http/Controllers/Api/SettingsController.php
+++ b/app/Http/Controllers/Api/SettingsController.php
@@ -150,8 +150,11 @@ class SettingsController extends Controller
if (!config('app.lock_passwords')) {
try {
Notification::send(Setting::first(), new MailTest());
+ Log::debug('Attempting to sending to '.config('mail.reply_to.address'));
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (\Exception $e) {
+ Log::error('Mail sent error using '.config('mail.reply_to.address') .': '. $e->getMessage());
+ Log::debug($e);
return response()->json(['message' => $e->getMessage()], 500);
}
}
@@ -315,4 +318,4 @@ class SettingsController extends Controller
}
-}
\ No newline at end of file
+}
diff --git a/app/Http/Controllers/Api/UploadedFilesController.php b/app/Http/Controllers/Api/UploadedFilesController.php
new file mode 100644
index 0000000000..cab6ff9992
--- /dev/null
+++ b/app/Http/Controllers/Api/UploadedFilesController.php
@@ -0,0 +1,220 @@
+]
+ */
+ 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 ]
+ */
+ 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 ]
+ */
+ 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 ]
+ */
+ 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);
+
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php
index 09dadbbd3c..a0d9aeaa30 100644
--- a/app/Http/Controllers/Api/UsersController.php
+++ b/app/Http/Controllers/Api/UsersController.php
@@ -6,6 +6,7 @@ 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;
@@ -19,9 +20,11 @@ use App\Models\Consumable;
use App\Models\License;
use App\Models\User;
use App\Notifications\CurrentInventory;
+use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;
@@ -67,6 +70,7 @@ class UsersController extends Controller
'users.notes',
'users.permissions',
'users.phone',
+ 'users.mobile',
'users.state',
'users.two_factor_enrolled',
'users.two_factor_optin',
@@ -80,7 +84,12 @@ class UsersController extends Controller
'users.autoassign_licenses',
'users.website',
- ])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
+ ])->with('manager')
+ ->with('groups')
+ ->with('userloc')
+ ->with('company')
+ ->with('department')
+ ->with('createdBy')
->withCount([
'assets as assets_count' => function(Builder $query) {
$query->withoutTrashed();
@@ -101,10 +110,26 @@ class UsersController extends Controller
$users = $users->where('users.activated', '=', $request->input('activated'));
}
+ if ($request->input('admins') == 'true') {
+ $users = $users->OnlyAdminsAndSuperAdmins();
+ }
+
+ if ($request->input('superadmins') == 'true') {
+ $users = $users->OnlySuperAdmins();
+ }
+
if ($request->filled('company_id')) {
$users = $users->where('users.company_id', '=', $request->input('company_id'));
}
+ if ($request->filled('phone')) {
+ $users = $users->where('users.phone', '=', $request->input('phone'));
+ }
+
+ if ($request->filled('mobile')) {
+ $users = $users->where('users.mobile', '=', $request->input('mobile'));
+ }
+
if ($request->filled('location_id')) {
$users = $users->where('users.location_id', '=', $request->input('location_id'));
}
@@ -206,11 +231,11 @@ class UsersController extends Controller
}
if ($request->filled('manages_users_count')) {
- $users->has('manages_users_count', '=', $request->input('manages_users_count'));
+ $users->has('managesUsers', '=', $request->input('manages_users_count'));
}
if ($request->filled('manages_locations_count')) {
- $users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
+ $users->has('managedLocations', '=', $request->input('manages_locations_count'));
}
if ($request->filled('autoassign_licenses')) {
@@ -277,6 +302,7 @@ class UsersController extends Controller
'manages_users_count',
'manages_locations_count',
'phone',
+ 'mobile',
'address',
'city',
'state',
@@ -474,8 +500,25 @@ class UsersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
- if ($request->filled('password')) {
- $user->password = bcrypt($request->input('password'));
+ // 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');
+ }
+
}
// We need to use has() instead of filled()
@@ -676,7 +719,6 @@ class UsersController extends Controller
$this->authorize('view', License::class);
if ($user = User::where('id', $id)->withTrashed()->first()) {
- $this->authorize('update', $user);
$licenses = $user->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
@@ -736,6 +778,25 @@ 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] []
+ */
+ 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.
*
@@ -772,4 +833,37 @@ class UsersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);
}
+
+
+ /**
+ * Run the LDAP sync command to import users from LDAP via API.
+ *
+ * @author A. Gianotto
+ * @since 8.2.2
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function syncLdapUsers(Request $request)
+ {
+ $this->authorize('update', User::class);
+ // Call Artisan LDAP import command.
+
+ Artisan::call('snipeit:ldap-sync', ['--location_id' => $request->input('location_id'), '--json_summary' => true]);
+
+ // Collect and parse JSON summary.
+ $ldap_results_json = Artisan::output();
+ $ldap_results = json_decode($ldap_results_json, true);
+
+ if (!$ldap_results) {
+ return response()->json(Helper::formatStandardApiResponse('error', null,trans('general.no_results')), 200);
+ }
+
+ // Direct user to appropriate status page.
+ if ($ldap_results['error']) {
+ return response()->json(Helper::formatStandardApiResponse('error', null, $ldap_results['error_message']), 200);
+ }
+
+ return response()->json(Helper::formatStandardApiResponse('success', null, $ldap_results['summary']), 200);
+
+ }
}
diff --git a/app/Http/Controllers/AssetMaintenancesController.php b/app/Http/Controllers/AssetMaintenancesController.php
deleted file mode 100644
index 8ab710b03b..0000000000
--- a/app/Http/Controllers/AssetMaintenancesController.php
+++ /dev/null
@@ -1,272 +0,0 @@
-
- * @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
- * the content for the asset maintenances listing, which is generated in getDatatable.
- *
- * @todo This should be replaced with middleware and/or policies
- * @see AssetMaintenancesController::getDatatable() method that generates the JSON response
- * @author Vincent Sposato
- * @version v1.0
- * @since [v1.8]
- */
- public function index() : View
- {
- $this->authorize('view', Asset::class);
- return view('asset_maintenances/index');
- }
-
- /**
- * Returns a form view to create a new asset maintenance.
- *
- * @see AssetMaintenancesController::postCreate() method that stores the data
- * @author Vincent Sposato
- * @version v1.0
- * @since [v1.8]
- * @return mixed
- */
- public function create() : View
- {
- $this->authorize('update', Asset::class);
- $asset = null;
-
- if ($asset = Asset::find(request('asset_id'))) {
- // 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('asset', $asset)
- ->with('assetMaintenanceType', $assetMaintenanceType)
- ->with('item', new AssetMaintenance);
- }
-
- /**
- * Validates and stores the new asset maintenance
- *
- * @see AssetMaintenancesController::getCreate() method for the form
- * @author Vincent Sposato
- * @version v1.0
- * @since [v1.8]
- */
- 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'));
-
- if ((! Company::isCurrentUserHasAccess($asset)) && ($asset != null)) {
- return static::getInsufficientPermissionsRedirect();
- }
-
- // 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());
- }
-
- /**
- * Returns a form view to edit a selected asset maintenance.
- *
- * @see AssetMaintenancesController::postEdit() method that stores the data
- * @author Vincent Sposato
- * @param int $assetMaintenanceId
- * @version v1.0
- * @since [v1.8]
- */
- public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
- {
- $this->authorize('update', Asset::class);
- 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('selectedAsset', null)
- ->with('assetMaintenanceType', $assetMaintenanceType)
- ->with('item', $maintenance);
- }
-
- /**
- * Validates and stores an update to an asset maintenance
- *
- * @see AssetMaintenancesController::postEdit() method that stores the data
- * @author Vincent Sposato
- * @param Request $request
- * @param int $assetMaintenanceId
- * @version v1.0
- * @since [v1.8]
- */
- public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
- {
- $this->authorize('update', Asset::class);
-
- 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');
- $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');
-
- if (($maintenance->completion_date == null)
- ) {
- if (($maintenance->asset_maintenance_time !== 0)
- || (! is_null($maintenance->asset_maintenance_time))
- ) {
- $maintenance->asset_maintenance_time = null;
- }
- }
-
- if (($maintenance->completion_date !== null)
- && ($maintenance->start_date !== '')
- && ($maintenance->start_date !== '0000-00-00')
- ) {
- $startDate = Carbon::parse($maintenance->start_date);
- $completionDate = Carbon::parse($maintenance->completion_date);
- $maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
- }
-
- // 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'));
- }
-
- return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
- }
-
- /**
- * Delete an asset maintenance
- *
- * @author Vincent Sposato
- * @param int $assetMaintenanceId
- * @version v1.0
- * @since [v1.8]
- */
- public function destroy($assetMaintenanceId) : RedirectResponse
- {
- $this->authorize('update', Asset::class);
- // 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
- $assetMaintenance->delete();
-
- // Redirect to the asset_maintenance management page
- return redirect()->route('maintenances.index')
- ->with('success', trans('admin/asset_maintenances/message.delete.success'));
- }
-
- /**
- * View an asset maintenance
- *
- * @author Vincent Sposato
- * @param int $assetMaintenanceId
- * @version v1.0
- * @since [v1.8]
- */
- 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);
- }
-}
diff --git a/app/Http/Controllers/AssetModelsController.php b/app/Http/Controllers/AssetModelsController.php
index 0795588776..b2745a6e53 100755
--- a/app/Http/Controllers/AssetModelsController.php
+++ b/app/Http/Controllers/AssetModelsController.php
@@ -87,7 +87,20 @@ class AssetModelsController extends Controller
$model->fieldset_id = $request->input('fieldset_id');
}
- $model = $request->handleImages($model);
+ 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);
+ }
+
+
if ($model->save()) {
if ($this->shouldAddDefaultValues($request->input())) {
@@ -271,7 +284,7 @@ class AssetModelsController extends Controller
->with('depreciation_list', Helper::depreciationList())
->with('item', $model)
->with('model_id', $model->id)
- ->with('clone_model', $cloned_model);
+ ->with('cloned_model', $cloned_model);
}
diff --git a/app/Http/Controllers/AssetModelsFilesController.php b/app/Http/Controllers/AssetModelsFilesController.php
deleted file mode 100644
index 14b2c1fc0b..0000000000
--- a/app/Http/Controllers/AssetModelsFilesController.php
+++ /dev/null
@@ -1,115 +0,0 @@
-]
- */
- public function store(UploadFileRequest $request, $modelId = null) : RedirectResponse
- {
- if (! $model = AssetModel::find($modelId)) {
- return redirect()->route('models.index')->with('error', trans('admin/hardware/message.does_not_exist'));
- }
-
- $this->authorize('update', $model);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/assetmodels')) {
- Storage::makeDirectory('private_uploads/assetmodels', 775);
- }
-
- foreach ($request->file('file') as $file) {
-
- $file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$model->id,$file);
-
- $model->logUpload($file_name, $request->get('notes'));
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
- }
-
- return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
- }
-
- /**
- * Check for permissions and display the file.
- *
- * @author [A. Gianotto] []
- * @param int $modelId
- * @param int $fileId
- * @since [v1.0]
- */
- public function show(AssetModel $model, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
- {
-
- $this->authorize('view', $model);
-
- if (! $log = Actionlog::find($fileId)) {
- return response('No matching record for that model/file', 500)
- ->header('Content-Type', 'text/plain');
- }
-
- $file = 'private_uploads/assetmodels/'.$log->filename;
-
- if (! Storage::exists($file)) {
- return response('File '.$file.' not found on server', 404)
- ->header('Content-Type', 'text/plain');
- }
-
- if (request('inline') == 'true') {
-
- $headers = [
- 'Content-Disposition' => 'inline',
- ];
-
- return Storage::download($file, $log->filename, $headers);
- }
-
- return StorageHelper::downloader($file);
- }
-
- /**
- * Delete the associated file
- *
- * @author [A. Gianotto] []
- * @param int $modelId
- * @param int $fileId
- * @since [v1.0]
- */
- public function destroy(AssetModel $model, $fileId = null) : RedirectResponse
- {
- $rel_path = 'private_uploads/assetmodels';
- $this->authorize('update', $model);
- $log = Actionlog::find($fileId);
- if ($log) {
- if (Storage::exists($rel_path.'/'.$log->filename)) {
- Storage::delete($rel_path.'/'.$log->filename);
- }
- $log->delete();
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
-
- }
-}
diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php
index 083975867b..62e8e59ca8 100644
--- a/app/Http/Controllers/Assets/AssetCheckinController.php
+++ b/app/Http/Controllers/Assets/AssetCheckinController.php
@@ -96,7 +96,6 @@ class AssetCheckinController extends Controller
});
$asset->expected_checkin = null;
- $asset->last_checkin = now();
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
$asset->name = $request->get('name');
@@ -123,11 +122,14 @@ 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]);
@@ -151,7 +153,8 @@ class AssetCheckinController extends Controller
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
- return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
+ return 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());
diff --git a/app/Http/Controllers/Assets/AssetCheckoutController.php b/app/Http/Controllers/Assets/AssetCheckoutController.php
index da2c0a2550..0ea9b36418 100644
--- a/app/Http/Controllers/Assets/AssetCheckoutController.php
+++ b/app/Http/Controllers/Assets/AssetCheckoutController.php
@@ -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 redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
+ return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
diff --git a/app/Http/Controllers/Assets/AssetFilesController.php b/app/Http/Controllers/Assets/AssetFilesController.php
deleted file mode 100644
index cf119edddc..0000000000
--- a/app/Http/Controllers/Assets/AssetFilesController.php
+++ /dev/null
@@ -1,108 +0,0 @@
-]
- */
- public function store(UploadFileRequest $request, Asset $asset) : RedirectResponse
- {
-
- $this->authorize('update', $asset);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/assets')) {
- Storage::makeDirectory('private_uploads/assets', 775);
- }
-
- foreach ($request->file('file') as $file) {
- $file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
-
- $asset->logUpload($file_name, $request->get('notes'));
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.upload.success'));
- }
-
- return redirect()->back()->with('error', trans('admin/hardware/message.upload.nofiles'));
- }
-
- /**
- * Check for permissions and display the file.
- *
- * @author [A. Gianotto] []
- * @param int $assetId
- * @param int $fileId
- * @since [v1.0]
- */
- public function show(Asset $asset, $fileId = null) : View | RedirectResponse | Response | StreamedResponse | BinaryFileResponse
- {
-
- $this->authorize('view', $asset);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
- $file = 'private_uploads/assets/'.$log->filename;
-
- if ($log->action_type == 'audit') {
- $file = 'private_uploads/audits/'.$log->filename;
- }
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('hardware.show', $asset)->with('error', trans('general.file_not_found'));
- }
-
- }
-
- return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
-
-
- }
-
- /**
- * Delete the associated file
- *
- * @author [A. Gianotto] []
- * @param int $assetId
- * @param int $fileId
- * @since [v1.0]
- */
- public function destroy(Asset $asset, $fileId = null) : RedirectResponse
- {
- $this->authorize('update', $asset);
- $rel_path = 'private_uploads/assets';
-
- if ($log = Actionlog::find($fileId)) {
- if (Storage::exists($rel_path.'/'.$log->filename)) {
- Storage::delete($rel_path.'/'.$log->filename);
- }
- $log->delete();
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- return redirect()->route('hardware.show', $asset)->with('error', trans('general.log_record_not_found'));
- }
-
-}
diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php
index 809605bccb..10f03db215 100755
--- a/app/Http/Controllers/Assets/AssetsController.php
+++ b/app/Http/Controllers/Assets/AssetsController.php
@@ -157,8 +157,16 @@ class AssetsController extends Controller
$asset->location_id = $request->input('rtd_location_id', null);
}
- // Create the image (if one was chosen.)
- if ($request->has('image')) {
+ 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 {
$asset = $request->handleImages($asset);
}
@@ -188,14 +196,31 @@ class AssetsController extends Controller
// Validate the asset before saving
if ($asset->isValid() && $asset->save()) {
- if (request('assigned_user')) {
- $target = User::find(request('assigned_user'));
+ $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'));
+ }
$location = $target->location_id;
- } elseif (request('assigned_asset')) {
- $target = Asset::find(request('assigned_asset'));
+
+ } 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'));
+ }
$location = $target->location_id;
- } elseif (request('assigned_location')) {
- $target = Location::find(request('assigned_location'));
+
+ } 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'));
+ }
$location = $target->id;
}
@@ -209,25 +234,32 @@ class AssetsController extends Controller
$failures[] = join(",", $asset->getErrors()->all());
}
}
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
+
+ session()->put(['checkout_to_type' => $request->get('checkout_to_type'),
+ '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 redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
+ return Helper::getRedirectOption($request, $asset->id, 'Assets') //FIXME - not tested
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
} else {
if (count($successes) == 1) {
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
//and re-translated
- return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
+ return 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 redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
+ return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
}
}
@@ -248,6 +280,7 @@ 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())
@@ -410,11 +443,15 @@ 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 redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
+ return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -619,8 +656,9 @@ class AssetsController extends Controller
*/
public function getClone(Asset $asset)
{
- $this->authorize('create', $asset);
+ $this->authorize('create', Asset::class);
$cloned = clone $asset;
+ $cloned_model = $asset;
$cloned->id = null;
$cloned->asset_tag = '';
$cloned->serial = '';
@@ -630,6 +668,7 @@ 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);
}
@@ -979,7 +1018,7 @@ class AssetsController extends Controller
}
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
- return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.audit.success'));
+ return Helper::getRedirectOption($request, $asset->id, 'Assets')->with('success', trans('admin/hardware/message.audit.success'));
}
return redirect()->back()->withInput()->withErrors($asset->getErrors());
diff --git a/app/Http/Controllers/Assets/BulkAssetsController.php b/app/Http/Controllers/Assets/BulkAssetsController.php
index 6fc25224f9..1787450e6f 100644
--- a/app/Http/Controllers/Assets/BulkAssetsController.php
+++ b/app/Http/Controllers/Assets/BulkAssetsController.php
@@ -52,11 +52,26 @@ 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]);
@@ -97,11 +112,47 @@ 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';
- $assets = Asset::with('assignedTo', 'location', 'model')
+ $query = Asset::with('assignedTo', 'location', 'model')
->whereIn('assets.id', $asset_ids)
->withTrashed();
- $assets = $assets->get();
+
+ 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();
if ($assets->isEmpty()) {
Log::debug('No assets were found for the provided IDs', ['ids' => $asset_ids]);
@@ -110,6 +161,7 @@ class BulkAssetsController extends Controller
$models = $assets->unique('model_id');
$modelNames = [];
+
foreach($models as $model) {
$modelNames[] = $model->model->name;
}
@@ -145,7 +197,6 @@ class BulkAssetsController extends Controller
case 'edit':
$this->authorize('update', Asset::class);
-
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
@@ -154,40 +205,7 @@ 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');
}
@@ -206,14 +224,26 @@ 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');
- if ($request->session()->has('bulk_back_url')) {
- $bulk_back_url = $request->session()->pull('bulk_back_url');
- }
+ $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->filled('ids') || count($request->input('ids')) == 0) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
@@ -251,7 +281,9 @@ 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
@@ -274,10 +306,14 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date')
- ->conditionallyAddItem('asset_eol_date');
+ ->conditionallyAddItem('asset_eol_date')
+ ->conditionallyAddItem('notes');
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')) {
@@ -328,6 +364,10 @@ class BulkAssetsController extends Controller
}
}
+ if ($request->input('null_notes')=='1') {
+ $this->update_array['notes'] = null;
+ }
+
if ($request->filled('purchase_cost')) {
@@ -368,10 +408,12 @@ 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.
- if (
- ($asset->assigned_to == '')
- || ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
- ) {
+
+ $unassigned = $asset->assigned_to == '';
+ $deployable = $updated_status->deployable == '1' && $asset->assetstatus?->deployable == '1';
+ $pending = $updated_status->pending === 1;
+
+ if ($unassigned || $deployable || $pending) {
$this->update_array['status_id'] = $updated_status->id;
}
@@ -423,6 +465,7 @@ class BulkAssetsController extends Controller
}
/**
+ *
* Start all the custom fields shenanigans
*/
@@ -430,6 +473,15 @@ 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});
@@ -488,7 +540,13 @@ class BulkAssetsController extends Controller
} // end asset foreach
if ($has_errors > 0) {
- return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array);
+ 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('success', trans('admin/hardware/message.update.success'));
@@ -613,7 +671,6 @@ class BulkAssetsController extends Controller
// See if there is a status label passed
if ($request->filled('status_id')) {
- \Log::error('status id: ' . $request->get('status_id'));
$asset->status_id = $request->get('status_id');
}
@@ -660,4 +717,54 @@ 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);
+ }
}
diff --git a/app/Http/Controllers/CategoriesController.php b/app/Http/Controllers/CategoriesController.php
index b934b3c33e..f627599541 100755
--- a/app/Http/Controllers/CategoriesController.php
+++ b/app/Http/Controllers/CategoriesController.php
@@ -76,6 +76,7 @@ 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();
@@ -129,6 +130,7 @@ 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');
diff --git a/app/Http/Controllers/CompaniesController.php b/app/Http/Controllers/CompaniesController.php
index 96a80e87e6..db6118d37f 100644
--- a/app/Http/Controllers/CompaniesController.php
+++ b/app/Http/Controllers/CompaniesController.php
@@ -123,11 +123,13 @@ 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')
diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php
index 379882c3c5..b784576122 100644
--- a/app/Http/Controllers/Components/ComponentCheckinController.php
+++ b/app/Http/Controllers/Components/ComponentCheckinController.php
@@ -100,8 +100,8 @@ class ComponentCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
- return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success',
- trans('admin/components/message.checkin.success'));
+ return 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'));
diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php
index b40d592369..4abf426de3 100644
--- a/app/Http/Controllers/Components/ComponentCheckoutController.php
+++ b/app/Http/Controllers/Components/ComponentCheckoutController.php
@@ -120,6 +120,7 @@ class ComponentCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
- return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.checkout.success'));
+ return Helper::getRedirectOption($request, $component->id, 'Components')
+ ->with('success', trans('admin/components/message.checkout.success'));
}
}
diff --git a/app/Http/Controllers/Components/ComponentsController.php b/app/Http/Controllers/Components/ComponentsController.php
index 74594d312b..ff1b0061b4 100644
--- a/app/Http/Controllers/Components/ComponentsController.php
+++ b/app/Http/Controllers/Components/ComponentsController.php
@@ -88,10 +88,16 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
- session()->put(['redirect_option' => $request->get('redirect_option')]);
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
+
if ($component->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
+ return Helper::getRedirectOption($request, $component->id, 'Components')
+ ->with('success', trans('admin/components/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
@@ -111,6 +117,7 @@ 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');
@@ -164,7 +171,8 @@ class ComponentsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
+ return Helper::getRedirectOption($request, $component->id, 'Components')
+ ->with('success', trans('admin/components/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
diff --git a/app/Http/Controllers/Components/ComponentsFilesController.php b/app/Http/Controllers/Components/ComponentsFilesController.php
deleted file mode 100644
index b5e30aa694..0000000000
--- a/app/Http/Controllers/Components/ComponentsFilesController.php
+++ /dev/null
@@ -1,138 +0,0 @@
-]
- * @since [v1.0]
- * @todo Switch to using the AssetFileRequest form request validator.
- */
- public function store(UploadFileRequest $request, $componentId = null)
- {
-
- if (config('app.lock_passwords')) {
- return redirect()->route('components.show', ['component'=>$componentId])->with('error', trans('general.feature_disabled'));
- }
-
- $component = Component::find($componentId);
-
- if (isset($component->id)) {
- $this->authorize('update', $component);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/components')) {
- Storage::makeDirectory('private_uploads/components', 775);
- }
-
- foreach ($request->file('file') as $file) {
- $file_name = $request->handleFile('private_uploads/components/','component-'.$component->id, $file);
-
- //Log the upload to the log
- $component->logUpload($file_name, e($request->input('notes')));
- }
-
-
- return redirect()->route('components.show', $component->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
-
- }
-
- return redirect()->route('components.show', $component->id)->with('error', trans('general.no_files_uploaded'));
- }
- // Prepare the error message
- return redirect()->route('components.index')
- ->with('error', trans('general.file_does_not_exist'));
- }
-
- /**
- * Deletes the selected component file.
- *
- * @author [A. Gianotto] []
- * @since [v1.0]
- * @param int $componentId
- * @param int $fileId
- * @return \Illuminate\Http\RedirectResponse
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function destroy($componentId = null, $fileId = null)
- {
- $component = Component::find($componentId);
-
- // the asset is valid
- if (isset($component->id)) {
- $this->authorize('update', $component);
- $log = Actionlog::find($fileId);
-
- // Remove the file if one exists
- if (Storage::exists('components/'.$log->filename)) {
- try {
- Storage::delete('components/'.$log->filename);
- } catch (\Exception $e) {
- Log::debug($e);
- }
- }
-
- $log->delete();
-
- return redirect()->back()->withFragment('files')
- ->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- // Redirect to the licence management page
- return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist'));
- }
-
- /**
- * Allows the selected file to be viewed.
- *
- * @author [A. Gianotto] []
- * @since [v1.4]
- * @param int $componentId
- * @param int $fileId
- * @return \Symfony\Component\HttpFoundation\Response
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function show($componentId = null, $fileId = null)
- {
- Log::debug('Private filesystem is: '.config('filesystems.default'));
-
-
- // the component is valid
- if ($component = Component::find($componentId)) {
- $this->authorize('view', $component);
- $this->authorize('components.files', $component);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $component->id)->find($fileId)) {
-
- $file = 'private_uploads/components/'.$log->filename;
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.file_not_found'));
- }
- }
- return redirect()->route('components.show', ['component' => $component])->with('error', trans('general.log_record_not_found'));
-
- }
-
- return redirect()->route('components.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
- }
-}
diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php
index e08da41229..3e972e1085 100644
--- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php
+++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php
@@ -111,6 +111,7 @@ class ConsumableCheckoutController extends Controller
// Redirect to the new consumable page
- return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
+ return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
+ ->with('success', trans('admin/consumables/message.checkout.success'));
}
}
diff --git a/app/Http/Controllers/Consumables/ConsumablesController.php b/app/Http/Controllers/Consumables/ConsumablesController.php
index c96c2db975..2601b69edd 100644
--- a/app/Http/Controllers/Consumables/ConsumablesController.php
+++ b/app/Http/Controllers/Consumables/ConsumablesController.php
@@ -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\Auth;
+use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
@@ -81,16 +81,33 @@ 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');
- $consumable = $request->handleImages($consumable);
+ if ($request->has('use_cloned_image')) {
+ $cloned_model_img = Consumable::select('image')->find($request->input('clone_image_from_id'));
+ if ($cloned_model_img) {
+ $new_image_name = 'clone-'.date('U').'-'.$cloned_model_img->image;
+ $new_image = 'consumables/'.$new_image_name;
+ Storage::disk('public')->copy('consumables/'.$cloned_model_img->image, $new_image);
+ $consumable->image = $new_image_name;
+ }
+
+ } else {
+ $consumable = $request->handleImages($consumable);
+ }
+
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
- session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
+ return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
+ ->with('success', trans('admin/consumables/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -107,6 +124,7 @@ 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');
@@ -160,7 +178,8 @@ class ConsumablesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
+ return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
+ ->with('success', trans('admin/consumables/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -210,9 +229,10 @@ 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('item', $consumable);
+ return view('consumables/edit')
+ ->with('cloned_model', $consumable_to_close)
+ ->with('item', $consumable);
}
}
diff --git a/app/Http/Controllers/Consumables/ConsumablesFilesController.php b/app/Http/Controllers/Consumables/ConsumablesFilesController.php
deleted file mode 100644
index 545b008dc0..0000000000
--- a/app/Http/Controllers/Consumables/ConsumablesFilesController.php
+++ /dev/null
@@ -1,134 +0,0 @@
-]
- * @since [v1.0]
- * @todo Switch to using the AssetFileRequest form request validator.
- */
- public function store(UploadFileRequest $request, $consumableId = null)
- {
- if (config('app.lock_passwords')) {
- return redirect()->route('consumables.show', ['consumable'=>$consumableId])->with('error', trans('general.feature_disabled'));
- }
-
- $consumable = Consumable::find($consumableId);
-
- if (isset($consumable->id)) {
- $this->authorize('update', $consumable);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/consumables')) {
- Storage::makeDirectory('private_uploads/consumables', 775);
- }
-
- foreach ($request->file('file') as $file) {
- $file_name = $request->handleFile('private_uploads/consumables/','consumable-'.$consumable->id, $file);
-
- //Log the upload to the log
- $consumable->logUpload($file_name, e($request->input('notes')));
- }
-
-
- return redirect()->route('consumables.show', $consumable->id)->withFragment('files')->with('success', trans('general.file_upload_success'));
-
- }
-
- return redirect()->route('consumables.show', $consumable->id)->with('error', trans('general.no_files_uploaded'));
- }
- // Prepare the error message
- return redirect()->route('consumables.index')
- ->with('error', trans('general.file_does_not_exist'));
- }
-
- /**
- * Deletes the selected consumable file.
- *
- * @author [A. Gianotto] []
- * @since [v1.0]
- * @param int $consumableId
- * @param int $fileId
- * @return \Illuminate\Http\RedirectResponse
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function destroy($consumableId = null, $fileId = null)
- {
- $consumable = Consumable::find($consumableId);
-
- // the asset is valid
- if (isset($consumable->id)) {
- $this->authorize('update', $consumable);
- $log = Actionlog::find($fileId);
-
- // Remove the file if one exists
- if (Storage::exists('consumables/'.$log->filename)) {
- try {
- Storage::delete('consumables/'.$log->filename);
- } catch (\Exception $e) {
- Log::debug($e);
- }
- }
-
- $log->delete();
-
- return redirect()->back()->withFragment('files')
- ->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- // Redirect to the licence management page
- return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist'));
- }
-
- /**
- * Allows the selected file to be viewed.
- *
- * @author [A. Gianotto] []
- * @since [v1.4]
- * @param int $consumableId
- * @param int $fileId
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function show($consumableId = null, $fileId = null)
- {
- $consumable = Consumable::find($consumableId);
-
- // the consumable is valid
- if (isset($consumable->id)) {
- $this->authorize('view', $consumable);
- $this->authorize('consumables.files', $consumable);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $consumable->id)->find($fileId)) {
- $file = 'private_uploads/consumables/'.$log->filename;
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.file_not_found'));
- }
- }
- // The log record doesn't exist somehow
- return redirect()->route('consumables.show', ['consumable' => $consumable])->with('error', trans('general.log_record_not_found'));
-
- }
-
- return redirect()->route('consumables.index')->with('error', trans('general.file_does_not_exist', ['id' => $fileId]));
- }
-}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index 74fff19a37..266769ffb2 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -22,6 +22,15 @@
namespace App\Http\Controllers;
+use App\Models\Accessory;
+use App\Models\Asset;
+use App\Models\AssetModel;
+use App\Models\Component;
+use App\Models\Consumable;
+use App\Models\License;
+use App\Models\Location;
+use App\Models\Maintenance;
+use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
@@ -32,6 +41,45 @@ abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+ static $map_object_type = [
+ 'accessories' => Accessory::class,
+ 'maintenances' => Maintenance::class,
+ 'assets' => Asset::class,
+ 'components' => Component::class,
+ 'consumables' => Consumable::class,
+ 'hardware' => Asset::class,
+ 'licenses' => License::class,
+ 'locations' => Location::class,
+ 'models' => AssetModel::class,
+ 'users' => User::class,
+ ];
+
+ static $map_storage_path = [
+ 'accessories' => 'private_uploads/accessories/',
+ 'maintenances' => 'private_uploads/maintenances/',
+ 'assets' => 'private_uploads/assets/',
+ 'components' => 'private_uploads/components/',
+ 'consumables' => 'private_uploads/consumables/',
+ 'hardware' => 'private_uploads/assets/',
+ 'licenses' => 'private_uploads/licenses/',
+ 'locations' => 'private_uploads/locations/',
+ 'models' => 'private_uploads/models/',
+ 'users' => 'private_uploads/users/',
+ ];
+
+ static $map_file_prefix= [
+ 'accessories' => 'accessory',
+ 'maintenances' => 'maintenance',
+ 'assets' => 'asset',
+ 'components' => 'component',
+ 'consumables' => 'consumable',
+ 'hardware' => 'asset',
+ 'licenses' => 'license',
+ 'locations' => 'location',
+ 'models' => 'model',
+ 'users' => 'user',
+ ];
+
public function __construct()
{
view()->share('signedIn', Auth::check());
diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php
index 72a3d43d51..2a17c06d62 100644
--- a/app/Http/Controllers/Licenses/LicenseCheckinController.php
+++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php
@@ -86,8 +86,10 @@ class LicenseCheckinController extends Controller
}
if($licenseSeat->assigned_to != null){
- $return_to = User::find($licenseSeat->assigned_to);
- session()->put('checkedInFrom', $return_to->id);
+ $return_to = User::withTrashed()->find($licenseSeat->assigned_to);
+ if ($return_to) {
+ session()->put('checkedInFrom', $return_to->id);
+ }
} else {
$return_to = Asset::find($licenseSeat->asset_id);
}
@@ -98,14 +100,17 @@ 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 redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
+ return Helper::getRedirectOption($request, $license->id, 'Licenses')
+ ->with('success', trans('admin/licenses/message.checkin.success'));
}
// Redirect to the license page with error
diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php
index 564ce97a89..e2225442b3 100644
--- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php
+++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php
@@ -89,7 +89,8 @@ class LicenseCheckoutController extends Controller
if ($checkoutTarget) {
- return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
+ return Helper::getRedirectOption($request, $license->id, 'Licenses')
+ ->with('success', trans('admin/licenses/message.checkout.success'));
}
diff --git a/app/Http/Controllers/Licenses/LicenseFilesController.php b/app/Http/Controllers/Licenses/LicenseFilesController.php
deleted file mode 100644
index 6ab3cb7703..0000000000
--- a/app/Http/Controllers/Licenses/LicenseFilesController.php
+++ /dev/null
@@ -1,132 +0,0 @@
-]
- * @since [v1.0]
- * @todo Switch to using the AssetFileRequest form request validator.
- */
- public function store(UploadFileRequest $request, $licenseId = null)
- {
- $license = License::find($licenseId);
-
- if (isset($license->id)) {
- $this->authorize('update', $license);
-
- if ($request->hasFile('file')) {
- if (! Storage::exists('private_uploads/licenses')) {
- Storage::makeDirectory('private_uploads/licenses', 775);
- }
-
- foreach ($request->file('file') as $file) {
- $file_name = $request->handleFile('private_uploads/licenses/','license-'.$license->id, $file);
-
- //Log the upload to the log
- $license->logUpload($file_name, e($request->input('notes')));
- }
-
-
- return redirect()->route('licenses.show', $license->id)->with('success', trans('admin/licenses/message.upload.success'));
-
- }
-
- return redirect()->route('licenses.show', $license->id)->with('error', trans('admin/licenses/message.upload.nofiles'));
- }
- // Prepare the error message
- return redirect()->route('licenses.index')
- ->with('error', trans('admin/licenses/message.does_not_exist'));
- }
-
- /**
- * Deletes the selected license file.
- *
- * @author [A. Gianotto] []
- * @since [v1.0]
- * @param int $licenseId
- * @param int $fileId
- * @return \Illuminate\Http\RedirectResponse
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function destroy($licenseId = null, $fileId = null)
- {
- if ($license = License::find($licenseId)) {
-
- $this->authorize('update', $license);
-
- if ($log = Actionlog::find($fileId)) {
-
- // Remove the file if one exists
- if (Storage::exists('licenses/'.$log->filename)) {
- try {
- Storage::delete('licenses/'.$log->filename);
- } catch (\Exception $e) {
- Log::debug($e);
- }
- }
-
- $log->delete();
-
- return redirect()->back()
- ->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- return redirect()->route('licenses.index')->with('error', trans('general.log_does_not_exist'));
- }
-
- return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist'));
- }
-
- /**
- * Allows the selected file to be viewed.
- *
- * @author [A. Gianotto] []
- * @since [v1.4]
- * @param int $licenseId
- * @param int $fileId
- * @return \Symfony\Component\HttpFoundation\Response
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function show($licenseId = null, $fileId = null, $download = true)
- {
- $license = License::find($licenseId);
-
- // the license is valid
- if (isset($license->id)) {
- $this->authorize('view', $license);
- $this->authorize('licenses.files', $license);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $license->id)->find($fileId)) {
- $file = 'private_uploads/licenses/'.$log->filename;
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.file_not_found'));
- }
- }
-
- // The log record doesn't exist somehow
- return redirect()->route('licenses.show', ['licenses' => $license])->with('error', trans('general.log_record_not_found'));
-
- }
-
- return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.does_not_exist', ['id' => $fileId]));
- }
-}
diff --git a/app/Http/Controllers/Licenses/LicensesController.php b/app/Http/Controllers/Licenses/LicensesController.php
index ab1e3868d2..d67f2a0be1 100755
--- a/app/Http/Controllers/Licenses/LicensesController.php
+++ b/app/Http/Controllers/Licenses/LicensesController.php
@@ -102,10 +102,15 @@ class LicensesController extends Controller
$license->created_by = auth()->id();
$license->min_amt = $request->input('min_amt');
- session()->put(['redirect_option' => $request->get('redirect_option')]);
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
if ($license->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
+ return Helper::getRedirectOption($request, $license->id, 'Licenses')
+ ->with('success', trans('admin/licenses/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($license->getErrors());
@@ -125,7 +130,7 @@ class LicensesController extends Controller
{
$this->authorize('update', $license);
-
+ session()->put('back_url', url()->previous());
$maintained_list = [
'' => 'Maintained',
'1' => 'Yes',
@@ -181,7 +186,8 @@ class LicensesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
- return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.update.success'));
+ return 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());
@@ -302,13 +308,16 @@ 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')
- ->orderBy('created_at', 'DESC');
+ 'assignedusers');
+ if (request()->filled('category_id')) {
+ $licenses = $licenses->where('category_id', request()->input('category_id'));
+ }
+ $licenses = $licenses->orderBy('created_at', 'DESC');
Company::scopeCompanyables($licenses)
->chunk(500, function ($licenses) use ($handle) {
$headers = [
diff --git a/app/Http/Controllers/LocationsController.php b/app/Http/Controllers/LocationsController.php
index da4e6a7e44..46eff73df7 100755
--- a/app/Http/Controllers/LocationsController.php
+++ b/app/Http/Controllers/LocationsController.php
@@ -96,7 +96,18 @@ class LocationsController extends Controller
$location->company_id = $request->input('company_id');
}
- $location = $request->handleImages($location);
+ 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);
+ }
if ($location->save()) {
return redirect()->route('locations.index')->with('success', trans('admin/locations/message.create.success'));
@@ -275,9 +286,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);
}
diff --git a/app/Http/Controllers/LocationsFilesController.php b/app/Http/Controllers/LocationsFilesController.php
deleted file mode 100644
index 3aaec0e089..0000000000
--- a/app/Http/Controllers/LocationsFilesController.php
+++ /dev/null
@@ -1,111 +0,0 @@
-]
- */
- public function store(UploadFileRequest $request, Location $location) : RedirectResponse
- {
- $this->authorize('update', $location);
-
- if ($request->hasFile('file')) {
-
- if (! Storage::exists('private_uploads/locations')) {
- Storage::makeDirectory('private_uploads/locations', 775);
- }
-
- foreach ($request->file('file') as $file) {
- $file_name = $request->handleFile('private_uploads/locations/','location-'.$location->id, $file);
- $location->logUpload($file_name, $request->get('notes'));
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
- }
-
- return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
- }
-
- /**
- * Check for permissions and display the file.
- *
- * @author [A. Gianotto] []
- * @param int $modelId
- * @param int $fileId
- * @since [v1.0]
- */
- public function show(Location $location, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
- {
-
- $this->authorize('view', $location);
-
- if (! $log = Actionlog::find($fileId)) {
- return redirect()->back()->withFragment('files')->with('error', 'No matching file record');
- }
-
- $file = 'private_uploads/locations/'.$log->filename;
-
- if (! Storage::exists($file)) {
- return redirect()->back()->withFragment('files')->with('error', 'No matching file on server');
- }
-
- if (request('inline') == 'true') {
-
- $headers = [
- 'Content-Disposition' => 'inline',
- ];
-
- return Storage::download($file, $log->filename, $headers);
- }
-
- return StorageHelper::downloader($file);
- }
-
- /**
- * Delete the associated file
- *
- * @author [A. Gianotto] []
- * @param int $modelId
- * @param int $fileId
- * @since [v1.0]
- */
- public function destroy(Location $location, $fileId = null) : RedirectResponse
- {
- $rel_path = 'private_uploads/locations';
- $this->authorize('update', $location);
- $log = Actionlog::find($fileId);
-
- if ($log) {
-
- // This should be moved to purge
-// if (Storage::exists($rel_path.'/'.$log->filename)) {
-// Storage::delete($rel_path.'/'.$log->filename);
-// }
- $log->delete();
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
-
- }
-}
diff --git a/app/Http/Controllers/MaintenancesController.php b/app/Http/Controllers/MaintenancesController.php
new file mode 100644
index 0000000000..e893b75f39
--- /dev/null
+++ b/app/Http/Controllers/MaintenancesController.php
@@ -0,0 +1,216 @@
+authorize('view', Asset::class);
+ return view('maintenances.index');
+ }
+
+ /**
+ * Returns a form view to create a new asset maintenance.
+ *
+ * @see MaintenancesController::postCreate() method that stores the data
+ * @author Vincent Sposato
+ * @version v1.0
+ * @since [v1.8]
+ * @return mixed
+ */
+ public function create() : View
+ {
+ $this->authorize('update', Asset::class);
+ $asset = null;
+
+ if ($asset = Asset::find(request('asset_id'))) {
+ // We have to set this so that the correct property is set in the select2 ajax dropdown
+ $asset->asset_id = $asset->id;
+ }
+
+ return view('maintenances/edit')
+ ->with('maintenanceType', Maintenance::getImprovementOptions())
+ ->with('asset', $asset)
+ ->with('item', new Maintenance);
+ }
+
+ /**
+ * Validates and stores the new asset maintenance
+ *
+ * @see MaintenancesController::getCreate() method for the form
+ * @author Vincent Sposato
+ * @version v1.0
+ * @since [v1.8]
+ */
+ public function store(ImageUploadRequest $request) : RedirectResponse
+ {
+ $this->authorize('update', Asset::class);
+
+ $assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
+
+ // Loop through the selected assets
+ foreach ($assets as $asset) {
+
+ $maintenance = new Maintenance();
+ $maintenance->supplier_id = $request->input('supplier_id');
+ $maintenance->is_warranty = $request->input('is_warranty');
+ $maintenance->cost = $request->input('cost');
+ $maintenance->notes = $request->input('notes');
+
+ // Save the asset maintenance data
+ $maintenance->asset_id = $asset->id;
+ $maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
+ $maintenance->name = $request->input('name');
+ $maintenance->start_date = $request->input('start_date');
+ $maintenance->completion_date = $request->input('completion_date');
+ $maintenance->created_by = auth()->id();
+
+ if (($maintenance->completion_date !== null)
+ && ($maintenance->start_date !== '')
+ && ($maintenance->start_date !== '0000-00-00')
+ ) {
+ $startDate = Carbon::parse($maintenance->start_date);
+ $completionDate = Carbon::parse($maintenance->completion_date);
+ $maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
+ }
+
+ $maintenance = $request->handleImages($maintenance);
+
+ // Was the asset maintenance created?
+ if (!$maintenance->save()) {
+ return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
+ }
+ }
+
+ return redirect()->route('maintenances.index')
+ ->with('success', trans('admin/maintenances/message.create.success'));
+
+ }
+
+ /**
+ * Returns a form view to edit a selected asset maintenance.
+ *
+ * @see MaintenancesController::postEdit() method that stores the data
+ * @author Vincent Sposato
+ * @version v1.0
+ * @since [v1.8]
+ */
+ public function edit(Maintenance $maintenance) : View | RedirectResponse
+ {
+ $this->authorize('update', Asset::class);
+ $this->authorize('update', $maintenance->asset);
+
+ return view('maintenances/edit')
+ ->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
+ ->with('asset_ids', request()->input('asset_ids', []))
+ ->with('maintenanceType', Maintenance::getImprovementOptions())
+ ->with('item', $maintenance);
+ }
+
+ /**
+ * Validates and stores an update to an asset maintenance
+ *
+ * @see MaintenancesController::postEdit() method that stores the data
+ * @author Vincent Sposato
+ * @param Request $request
+ * @param int $maintenanceId
+ * @version v1.0
+ * @since [v1.8]
+ */
+ public function update(ImageUploadRequest $request, Maintenance $maintenance) : View | RedirectResponse
+ {
+ $this->authorize('update', Asset::class);
+ $this->authorize('update', $maintenance->asset);
+
+ $maintenance->supplier_id = $request->input('supplier_id');
+ $maintenance->is_warranty = $request->input('is_warranty', 0);
+ $maintenance->cost = $request->input('cost');
+ $maintenance->notes = $request->input('notes');
+ $maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
+ $maintenance->name = $request->input('name');
+ $maintenance->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->asset_maintenance_time !== 0)
+ || (! is_null($maintenance->asset_maintenance_time))
+ ) {
+ $maintenance->asset_maintenance_time = null;
+ }
+ }
+
+ if (($maintenance->completion_date !== null)
+ && ($maintenance->start_date !== '')
+ && ($maintenance->start_date !== '0000-00-00')
+ ) {
+ $startDate = Carbon::parse($maintenance->start_date);
+ $completionDate = Carbon::parse($maintenance->completion_date);
+ $maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
+ }
+ $maintenance = $request->handleImages($maintenance);
+
+ if ($maintenance->save()) {
+ return redirect()->route('maintenances.index')
+ ->with('success', trans('admin/maintenances/message.edit.success'));
+ }
+
+ return redirect()->back()->withInput()->withErrors($maintenance->getErrors());
+ }
+
+ /**
+ * Delete an asset maintenance
+ *
+ * @author Vincent Sposato
+ * @param int $maintenanceId
+ * @version v1.0
+ * @since [v1.8]
+ */
+ public function destroy(Maintenance $maintenance) : RedirectResponse
+ {
+ $this->authorize('update', Asset::class);
+ $this->authorize('update', $maintenance->asset);
+ // Delete the asset maintenance
+ $maintenance->delete();
+ // Redirect to the asset_maintenance management page
+ return redirect()->route('maintenances.index')
+ ->with('success', trans('admin/maintenances/message.delete.success'));
+ }
+
+ /**
+ * View an asset maintenance
+ *
+ * @author Vincent Sposato
+ * @param int $maintenanceId
+ * @version v1.0
+ * @since [v1.8]
+ */
+ public function show(Maintenance $maintenance) : View | RedirectResponse
+ {
+ return view('maintenances.view')->with('maintenance', $maintenance);
+ }
+}
diff --git a/app/Http/Controllers/ManufacturersController.php b/app/Http/Controllers/ManufacturersController.php
index 584be275a9..965f49be30 100755
--- a/app/Http/Controllers/ManufacturersController.php
+++ b/app/Http/Controllers/ManufacturersController.php
@@ -59,7 +59,7 @@ class ManufacturersController extends Controller
$manufacturers_count = Manufacturer::withTrashed()->count();
if ($manufacturers_count == 0) {
- Artisan::call('db:seed', ['--class' => 'ManufacturerSeeder']);
+ Artisan::call('db:seed', ['--class' => 'Database\\Seeders\\ManufacturerSeeder', '--force' => true]);
return redirect()->route('manufacturers.index')->with('success', trans('general.seeding.manufacturers.success'));
}
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
index bbbc46da6f..a2fe612b6e 100755
--- a/app/Http/Controllers/ProfileController.php
+++ b/app/Http/Controllers/ProfileController.php
@@ -3,15 +3,21 @@
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.
@@ -220,7 +226,7 @@ class ProfileController extends Controller
if (!$user = User::find(auth()->id())) {
return redirect()->back()
- ->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
+ ->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
}
if (empty($user->email)) {
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
@@ -234,4 +240,28 @@ 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'));
+
+ }
}
diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php
index 4302afecbe..954b7cb64c 100644
--- a/app/Http/Controllers/ReportsController.php
+++ b/app/Http/Controllers/ReportsController.php
@@ -9,7 +9,7 @@ use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
-use App\Models\AssetMaintenance;
+use App\Models\Maintenance;
use App\Models\CheckoutAcceptance;
use App\Models\Company;
use App\Models\CustomField;
@@ -17,13 +17,11 @@ use App\Models\Depreciation;
use App\Models\License;
use App\Models\ReportTemplate;
use App\Models\Setting;
-use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Mail;
-use Illuminate\Support\Facades\Notification;
use \Illuminate\Contracts\View\View;
use League\Csv\Reader;
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -184,7 +182,7 @@ class ReportsController extends Controller
$currency = e(Setting::getSettings()->default_currency);
}
- $row[] = $asset->purchase_date;
+ $row[] = Helper::getFormattedDateObject($asset->purchase_date, 'date', false);
$row[] = $currency.Helper::formatCurrencyOutput($asset->purchase_cost);
$row[] = $currency.Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$row[] = $currency.Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
@@ -1038,11 +1036,11 @@ class ReportsController extends Controller
* @author Vincent Sposato
* @version v1.0
*/
- public function getAssetMaintenancesReport() : View
+ public function getMaintenancesReport() : View
{
$this->authorize('reports.view');
- return view('reports.asset_maintenances');
+ return view('reports.maintenances');
}
/**
@@ -1051,11 +1049,11 @@ class ReportsController extends Controller
* @author Vincent Sposato
* @version v1.0
*/
- public function exportAssetMaintenancesReport() : Response
+ public function exportMaintenancesReport() : Response
{
$this->authorize('reports.view');
// Grab all the improvements
- $assetMaintenances = AssetMaintenance::with('asset', 'supplier')
+ $Maintenances = Maintenance::with('asset', 'supplier')
->orderBy('created_at', 'DESC')
->get();
@@ -1063,36 +1061,36 @@ class ReportsController extends Controller
$header = [
trans('admin/hardware/table.asset_tag'),
- trans('admin/asset_maintenances/table.asset_name'),
+ trans('admin/maintenances/table.asset_name'),
trans('general.supplier'),
- trans('admin/asset_maintenances/form.asset_maintenance_type'),
- trans('admin/asset_maintenances/form.title'),
- trans('admin/asset_maintenances/form.start_date'),
- trans('admin/asset_maintenances/form.completion_date'),
- trans('admin/asset_maintenances/form.asset_maintenance_time'),
- trans('admin/asset_maintenances/form.cost'),
+ trans('admin/maintenances/form.asset_maintenance_type'),
+ trans('admin/maintenances/form.title'),
+ trans('admin/maintenances/form.start_date'),
+ trans('admin/maintenances/form.completion_date'),
+ trans('admin/maintenances/form.asset_maintenance_time'),
+ trans('admin/maintenances/form.cost'),
];
$header = array_map('trim', $header);
$rows[] = implode(',', $header);
- foreach ($assetMaintenances as $assetMaintenance) {
+ foreach ($Maintenances as $maintenance) {
$row = [];
- $row[] = str_replace(',', '', e($assetMaintenance->asset->asset_tag));
- $row[] = str_replace(',', '', e($assetMaintenance->asset->name));
- $row[] = str_replace(',', '', e($assetMaintenance->supplier->name));
- $row[] = e($assetMaintenance->improvement_type);
- $row[] = e($assetMaintenance->title);
- $row[] = e($assetMaintenance->start_date);
- $row[] = e($assetMaintenance->completion_date);
- if (is_null($assetMaintenance->asset_maintenance_time)) {
+ $row[] = str_replace(',', '', e($maintenance->asset->asset_tag));
+ $row[] = str_replace(',', '', e($maintenance->asset->name));
+ $row[] = str_replace(',', '', e($maintenance->supplier->name));
+ $row[] = e($maintenance->improvement_type);
+ $row[] = e($maintenance->name);
+ $row[] = e($maintenance->start_date);
+ $row[] = e($maintenance->completion_date);
+ if (is_null($maintenance->asset_maintenance_time)) {
$improvementTime = (int) Carbon::now()
- ->diffInDays(Carbon::parse($assetMaintenance->start_date), true);
+ ->diffInDays(Carbon::parse($maintenance->start_date), true);
} else {
- $improvementTime = (int) $assetMaintenance->asset_maintenance_time;
+ $improvementTime = (int) $maintenance->asset_maintenance_time;
}
$row[] = $improvementTime;
- $row[] = trans('general.currency') . Helper::formatCurrencyOutput($assetMaintenance->cost);
+ $row[] = trans('general.currency') . Helper::formatCurrencyOutput($maintenance->cost);
$rows[] = implode(',', $row);
}
diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php
index 747f7b7284..56b36470d4 100644
--- a/app/Http/Controllers/SettingsController.php
+++ b/app/Http/Controllers/SettingsController.php
@@ -352,6 +352,7 @@ 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') != '') {
@@ -650,6 +651,7 @@ 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');
@@ -922,7 +924,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'));
}
@@ -1082,6 +1084,7 @@ class SettingsController extends Controller
if (! config('app.lock_passwords')) {
if (Storage::exists($path.'/'.$filename)) {
+ Log::warning('User '.auth()->user()->username.' is attempting to download backup file: '.$filename);
return StorageHelper::downloader($path.'/'.$filename);
} else {
// Redirect to the backup page
@@ -1109,6 +1112,7 @@ class SettingsController extends Controller
if (Storage::exists($path . '/' . $filename)) {
try {
+ Log::warning('User '.auth()->user()->username.' is attempting to delete backup file: '.$filename);
Storage::delete($path . '/' . $filename);
return redirect()->route('settings.backups.index')->with('success', trans('admin/settings/message.backup.file_deleted'));
} catch (\Exception $e) {
@@ -1188,7 +1192,7 @@ class SettingsController extends Controller
'--force' => true,
]);
- Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
+ Log::warning('User '.auth()->user()->username.' is attempting to restore from: '. storage_path($path).'/'.$filename);
$restore_params = [
'--force' => true,
@@ -1337,9 +1341,11 @@ class SettingsController extends Controller
'name' => config('mail.from.name'),
'email' => config('mail.from.address'),
])->notify(new MailTest());
-
+ Log::debug('Attempting to send mail to '.config('mail.from.address'));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('mail_sent.mail_sent')));
} catch (\Exception $e) {
+ Log::error('Mail sent from '.config('mail.from.address') .' with errors '. $e->getMessage());
+ Log::debug($e);
return response()->json(Helper::formatStandardApiResponse('success', null, $e->getMessage()));
}
}
diff --git a/app/Http/Controllers/SuppliersController.php b/app/Http/Controllers/SuppliersController.php
index e6671808ce..c824bc1ffa 100755
--- a/app/Http/Controllers/SuppliersController.php
+++ b/app/Http/Controllers/SuppliersController.php
@@ -8,7 +8,6 @@ use App\Exceptions\ModelStillHasAssets;
use App\Exceptions\ModelStillHasLicenses;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Supplier;
-use Illuminate\Support\Facades\Auth;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
diff --git a/app/Http/Controllers/UploadedFilesController.php b/app/Http/Controllers/UploadedFilesController.php
new file mode 100644
index 0000000000..eb1c45110d
--- /dev/null
+++ b/app/Http/Controllers/UploadedFilesController.php
@@ -0,0 +1,162 @@
+]
+ */
+ public function store(UploadFileRequest $request, $object_type, $id) : RedirectResponse
+ {
+
+ // Check the permissions to make sure the user can view the object
+ $object = self::$map_object_type[$object_type]::find($id);
+ $this->authorize('update', $object);
+
+ if (!$object) {
+ return redirect()->back()->withFragment('files')->with('error',trans('general.file_upload_status.invalid_object'));
+ }
+
+ // If the file storage directory doesn't exist, create it
+ if (! Storage::exists(self::$map_storage_path[$object_type])) {
+ Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
+ }
+
+
+ if ($request->hasFile('file')) {
+ // Loop over the attached files and add them to the object
+ foreach ($request->file('file') as $file) {
+ $file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
+ $files[] = $file_name;
+ $object->logUpload($file_name, $request->get('notes'));
+ }
+
+ $files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
+ ->where('item_type', '=', self::$map_object_type[$object_type])
+ ->where('item_id', '=', $id)->whereIn('filename', $files)
+ ->get();
+
+ return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.upload.success', count($files)));
+ }
+
+ // No files were submitted
+ return redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.nofiles'));
+ }
+
+
+
+ /**
+ * Check for permissions and display the file.
+ * This isn't currently used, but is here for future use.
+ *
+ * @param \App\Http\Requests\UploadFileRequest $request
+ * @param string $object_type the type of object to upload the file to
+ * @param int $id the ID of the object to delete from so we can check permisisons
+ * @param $file_id the ID of the file to show from the action_logs table
+ * @since [v8.2.2]
+ * @author [A. Gianotto ]
+ */
+ public function show($object_type, $id, $file_id) : RedirectResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
+ {
+ // Check the permissions to make sure the user can view the object
+ $object = self::$map_object_type[$object_type]::find($id);
+ $this->authorize('view', $object);
+
+ if (!$object) {
+ return redirect()->back()->withFragment('files')->with('error',trans('general.file_upload_status.invalid_object'));
+ }
+
+
+ // Check that the file being requested exists for the object
+ if (! $log = Actionlog::whereNotNull('filename')->where('item_type', self::$map_object_type[$object_type])->where('item_id', $object->id)->find($file_id))
+ {
+ return redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.invalid_id'));
+ }
+
+
+ if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename))
+ {
+ return redirect()->back()->withFragment('files')->with('error', trans('general.file_upload_status.file_not_found'));
+ }
+
+ if (request('inline') == 'true') {
+ $headers = [
+ 'Content-Disposition' => 'inline',
+ ];
+ return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
+ }
+
+ return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
+
+ }
+
+ /**
+ * Delete the associated file
+ *
+ * @param \App\Http\Requests\UploadFileRequest $request
+ * @param string $object_type the type of object to upload the file to
+ * @param int $id the ID of the object to delete from so we can check permisisons
+ * @param $file_id the ID of the file to delete from the action_logs table
+ * @since [v8.2.2]
+ * @author [A. Gianotto ]
+ */
+ public function destroy($object_type, $id, $file_id) : RedirectResponse
+ {
+
+ // Check the permissions to make sure the user can view the object
+ $object = self::$map_object_type[$object_type]::find($id);
+ $this->authorize('update', self::$map_object_type[$object_type]);
+
+ if (!$object) {
+ return redirect()->back()->withFragment('files')->with('error',trans('general.file_upload_status.invalid_object'));
+ }
+
+
+ // Check for the file
+ $log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
+ ->where('item_id', $object->id)->first();
+
+ if ($log) {
+ // Check the file actually exists, and delete it
+ if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
+ Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
+ }
+ // Delete the record of the file
+ if ($log->delete()) {
+ return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.delete.success', 1));
+ }
+
+ }
+
+ // The file doesn't seem to really exist, so report an error
+ return redirect()->back()->withFragment('files')->with('success', trans_choice('general.file_upload_status.delete.error', 1));
+
+ }
+
+}
diff --git a/app/Http/Controllers/Users/UserFilesController.php b/app/Http/Controllers/Users/UserFilesController.php
deleted file mode 100644
index 45bd0c6329..0000000000
--- a/app/Http/Controllers/Users/UserFilesController.php
+++ /dev/null
@@ -1,129 +0,0 @@
-]
- * @since [v1.6]
- */
- public function store(UploadFileRequest $request, User $user)
- {
- $this->authorize('update', $user);
- $files = $request->file('file');
-
- if (is_null($files)) {
- return redirect()->back()->with('error', trans('admin/users/message.upload.nofiles'));
- }
- foreach ($files as $file) {
- $file_name = $request->handleFile('private_uploads/users/', 'user-'.$user->id, $file);
-
- //Log the uploaded file to the log
- $logAction = new Actionlog();
- $logAction->item_id = $user->id;
- $logAction->item_type = User::class;
- $logAction->created_by = auth()->id();
- $logAction->note = $request->input('notes');
- $logAction->target_id = null;
- $logAction->created_at = date("Y-m-d H:i:s");
- $logAction->filename = $file_name;
- $logAction->action_type = 'uploaded';
-
- if (! $logAction->save()) {
- return JsonResponse::create(['error' => 'Failed validation: '.print_r($logAction->getErrors(), true)], 500);
- }
-
- return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.upload.success'));
- }
-
-
- }
-
- /**
- * Delete file
- *
- * @author [A. Gianotto] []
- * @since [v1.6]
- * @param int $userId
- * @param int $fileId
- * @return \Illuminate\Http\RedirectResponse
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function destroy($userId = null, $fileId = null)
- {
- if ($user = User::find($userId)) {
-
- $this->authorize('delete', $user);
- $rel_path = 'private_uploads/users';
-
-
- if ($log = Actionlog::find($fileId)) {
- $filename = $log->filename;
- $log->delete();
-
- if (Storage::exists($rel_path.'/'.$filename)) {
- Storage::delete($rel_path.'/'.$filename);
- return redirect()->back()->withFragment('files')->with('success', trans('admin/users/message.deletefile.success'));
- }
-
- }
-
- // The log record doesn't exist somehow
- return redirect()->back()->with('success', trans('admin/users/message.deletefile.success'));
- }
-
- return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found', ['id' => $userId]));
-
- }
-
- /**
- * Display/download the uploaded file
- *
- * @author [A. Gianotto] []
- * @since [v1.6]
- * @param int $userId
- * @param int $fileId
- * @return mixed
- * @throws \Illuminate\Auth\Access\AuthorizationException
- */
- public function show(User $user, $fileId = null)
- {
-
-
- if (empty($fileId)) {
- return redirect()->route('users.show')->with('error', 'Invalid file request');
- }
-
- $this->authorize('view', $user);
-
- if ($log = Actionlog::whereNotNull('filename')->where('item_id', $user->id)->find($fileId)) {
- $file = 'private_uploads/users/'.$log->filename;
-
- try {
- return StorageHelper::showOrDownloadFile($file, $log->filename);
- } catch (\Exception $e) {
- return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.file_not_found'));
- }
- }
-
- // The log record doesn't exist somehow
- return redirect()->route('users.show', ['user' => $user])->with('error', trans('general.log_record_not_found'));
-
- }
-
-}
diff --git a/app/Http/Controllers/Users/UsersController.php b/app/Http/Controllers/Users/UsersController.php
index b18a7b6fff..2275e565cb 100755
--- a/app/Http/Controllers/Users/UsersController.php
+++ b/app/Http/Controllers/Users/UsersController.php
@@ -13,15 +13,8 @@ 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;
@@ -105,6 +98,7 @@ class UsersController extends Controller
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle');
$user->phone = $request->input('phone');
+ $user->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->department_id = $request->input('department_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
@@ -130,10 +124,15 @@ class UsersController extends Controller
}
$user->permissions = json_encode($permissions_array);
- // we have to invoke the
+ // we have to invoke the form request here to handle image uploads
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
- session()->put(['redirect_option' => $request->get('redirect_option')]);
+ if($request->get('redirect_option') === 'back'){
+ session()->put(['redirect_option' => 'index']);
+ } else {
+ session()->put(['redirect_option' => $request->get('redirect_option')]);
+ }
+
if ($user->save()) {
if ($request->filled('groups')) {
@@ -142,19 +141,8 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
- 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 Helper::getRedirectOption($request, $user->id, 'Users')
+ ->with('success', trans('admin/users/message.success.create'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -186,6 +174,7 @@ 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) {
@@ -246,22 +235,17 @@ 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->mobile = $request->input('mobile');
$user->location_id = $request->input('location_id', null);
$user->company_id = Company::getIdForUser($request->input('company_id', null));
$user->manager_id = $request->input('manager_id', null);
@@ -271,8 +255,6 @@ 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);
@@ -281,30 +263,49 @@ 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)]);
- // Do we want to update the user password?
- if ($request->filled('password')) {
- $user->password = bcrypt($request->input('password'));
+ // 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'));
+ }
}
+
// 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');
@@ -312,7 +313,7 @@ class UsersController extends Controller
if ($user->save()) {
// Redirect to the user page
- return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
+ return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.update'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -438,7 +439,7 @@ class UsersController extends Controller
app('request')->request->set('permissions', $permissions);
- $user_to_clone = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($user->id);
+ $user_to_clone = User::with('userloc')->withTrashed()->find($user->id);
// Make sure they can view this particular user
$this->authorize('view', $user_to_clone);
@@ -453,6 +454,8 @@ 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');
@@ -468,7 +471,7 @@ class UsersController extends Controller
->with('user', $user)
->with('groups', Group::pluck('name', 'id'))
->with('userGroups', $userGroups)
- ->with('clone_user', $user_to_clone)
+ ->with('cloned_model', $user_to_clone)
->with('item', $user);
}
@@ -510,6 +513,8 @@ 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'),
@@ -555,6 +560,8 @@ 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,
diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php
index bbff6ba4f7..c4e72971b4 100755
--- a/app/Http/Controllers/ViewAssetsController.php
+++ b/app/Http/Controllers/ViewAssetsController.php
@@ -27,50 +27,126 @@ use Exception;
class ViewAssetsController extends Controller
{
/**
- * Redirect to the profile page.
+ * 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.
*
*/
- public function getIndex() : View | RedirectResponse
+ public function getIndex(Request $request) : View | RedirectResponse
{
- $user = User::with(
+ $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([
'assets',
'assets.model',
'assets.model.fieldset.fields',
'consumables',
'accessories',
- '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;
- }
-
- }
- }
+ 'licenses'
+ ])->find($selectedUserId);
+ // 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'));
}
- // 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);
+ // Process custom fields for the user being viewed
+ $fieldArray = $this->extractCustomFields($userToView);
- 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));
+ // 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
+ ]);
}
/**
diff --git a/app/Http/Requests/ImageUploadRequest.php b/app/Http/Requests/ImageUploadRequest.php
index abb0cee5f7..3a62212e36 100644
--- a/app/Http/Requests/ImageUploadRequest.php
+++ b/app/Http/Requests/ImageUploadRequest.php
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Storage;
use Intervention\Image\Exception\NotReadableException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Str;
class ImageUploadRequest extends Request
{
@@ -70,19 +71,25 @@ class ImageUploadRequest extends Request
public function handleImages($item, $w = 600, $form_fieldname = 'image', $path = null, $db_fieldname = 'image')
{
- $type = strtolower(class_basename(get_class($item)));
+ $type = class_basename(get_class($item));
if (is_null($path)) {
- $path = str_plural($type);
+ $path = strtolower(str_plural($type));
- if ($type == 'assetmodel') {
+ if ($type == 'AssetModel') {
$path = 'models';
}
if ($type == 'user') {
$path = 'avatars';
}
+
+ }
+
+
+ if (!Storage::disk('public')->exists($path)) {
+ Storage::disk('public')->makeDirectory($path);
}
if ($this->offsetGet($form_fieldname) instanceof UploadedFile) {
@@ -93,10 +100,9 @@ class ImageUploadRequest extends Request
if (isset($image)) {
- if (!config('app.lock_passwords')) {
$ext = $image->guessExtension();
- $file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
+ $file_name = $type.'-'.$form_fieldname.($item->id ?? '-'.$item->id).'-'.str_random(10).'.'.$ext;
if (($image->getMimeType() == 'image/vnd.microsoft.icon') || ($image->getMimeType() == 'image/x-icon') || ($image->getMimeType() == 'image/avif') || ($image->getMimeType() == 'image/webp')) {
// If the file is an icon, webp or avif, we need to just move it since gd doesn't support resizing
@@ -138,7 +144,7 @@ class ImageUploadRequest extends Request
// Remove Current image if exists
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
$item->{$db_fieldname} = $file_name;
- }
+
// If the user isn't uploading anything new but wants to delete their old image, do so
diff --git a/app/Http/Requests/SettingsSamlRequest.php b/app/Http/Requests/SettingsSamlRequest.php
index 2ab876141a..f5483df9d2 100644
--- a/app/Http/Requests/SettingsSamlRequest.php
+++ b/app/Http/Requests/SettingsSamlRequest.php
@@ -41,6 +41,7 @@ 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)) {
@@ -56,7 +57,7 @@ class SettingsSamlRequest extends FormRequest
}
}
- $was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
+ $was_custom_x509cert = strpos($setting->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert = '';
$custom_privateKey = '';
@@ -126,10 +127,14 @@ class SettingsSamlRequest extends FormRequest
}
if (! (empty($x509cert) && empty($privateKey))) {
- $this->merge([
- 'saml_sp_x509cert' => $x509cert,
- 'saml_sp_privatekey' => $privateKey,
- ]);
+// $this->merge([
+// 'saml_sp_x509cert' => $x509cert,
+// 'saml_sp_privatekey' => $privateKey,
+// ]);
+ $setting->saml_sp_x509cert = $x509cert;
+ $setting->saml_sp_privatekey = $privateKey;
+ $setting->save();
+
}
} else {
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
@@ -145,15 +150,21 @@ class SettingsSamlRequest extends FormRequest
}
if (! empty($x509certNew)) {
- $this->merge([
- 'saml_sp_x509certNew' => $x509certNew,
- ]);
+// $this->merge([
+// 'saml_sp_x509certNew' => $x509certNew,
+// ]);
+ $setting->saml_sp_x509certNew = $x509certNew;
+ $setting->save();
}
} else {
- $this->merge([
- 'saml_sp_x509certNew' => '',
- ]);
+// $this->merge([
+// 'saml_sp_x509certNew' => '',
+// ]);
+ $setting->saml_sp_x509certNew = '';
+ $setting->save();
}
+
+
});
}
}
diff --git a/app/Http/Requests/StoreNotificationSettings.php b/app/Http/Requests/StoreNotificationSettings.php
index bf5f5b3d4e..f58d014c76 100644
--- a/app/Http/Requests/StoreNotificationSettings.php
+++ b/app/Http/Requests/StoreNotificationSettings.php
@@ -5,6 +5,7 @@ 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
{
@@ -26,6 +27,9 @@ 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',
diff --git a/app/Http/Requests/UploadFileRequest.php b/app/Http/Requests/UploadFileRequest.php
index e58f1a1be3..82f4a35be1 100644
--- a/app/Http/Requests/UploadFileRequest.php
+++ b/app/Http/Requests/UploadFileRequest.php
@@ -6,6 +6,7 @@ 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
{
@@ -27,44 +28,76 @@ class UploadFileRequest extends Request
*/
public function rules()
{
- $max_file_size = \App\Helpers\Helper::file_upload_max_size();
+ $max_file_size = Helper::file_upload_max_size();
return [
- 'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
+ 'file.*' => 'required|mimes:'.config('filesystems.allowed_upload_extensions_for_validator').'|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') {
- 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);
- }
-
+ $uploaded_file = $this->handleSVG($file);
} else {
- $put_results = Storage::put($dirname.$file_name, file_get_contents($file));
+ $uploaded_file = 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;
+
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php
index 702ea123d8..64086ca46e 100644
--- a/app/Http/Transformers/ActionlogsTransformer.php
+++ b/app/Http/Transformers/ActionlogsTransformer.php
@@ -2,6 +2,7 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
+use App\Helpers\StorageHelper;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CustomField;
@@ -16,6 +17,7 @@ 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
{
@@ -113,8 +115,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): '';
- $clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): '';
+ $clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old, ['allowed_classes' => false]) : '';
+ $clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new, ['allowed_classes' => false]) : '';
}
}
@@ -133,24 +135,6 @@ 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,
@@ -158,8 +142,10 @@ class ActionlogsTransformer
'file' => ($actionlog->filename!='')
?
[
- 'url' => $file_url,
+ 'url' => $actionlog->uploads_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) ? [
@@ -198,8 +184,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' => ($actionlog->remote_ip) ?? null,
- 'user_agent' => ($actionlog->user_agent) ?? null,
+ 'remote_ip' => e($actionlog->remote_ip) ?? null,
+ 'user_agent' => e($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'),
];
diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php
index 3f1e4ac8c2..976aff94f1 100644
--- a/app/Http/Transformers/AssetsTransformer.php
+++ b/app/Http/Transformers/AssetsTransformer.php
@@ -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->getLinearDepreciatedValue()),
+ 'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
];
@@ -203,6 +203,7 @@ 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;
}
diff --git a/app/Http/Transformers/DatatablesTransformer.php b/app/Http/Transformers/DatatablesTransformer.php
index 0e69109391..2ec993536d 100644
--- a/app/Http/Transformers/DatatablesTransformer.php
+++ b/app/Http/Transformers/DatatablesTransformer.php
@@ -4,6 +4,10 @@ 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);
@@ -11,4 +15,15 @@ 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;
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Transformers/DepreciationsTransformer.php b/app/Http/Transformers/DepreciationsTransformer.php
index 64d4c88f7e..af153539e2 100644
--- a/app/Http/Transformers/DepreciationsTransformer.php
+++ b/app/Http/Transformers/DepreciationsTransformer.php
@@ -26,11 +26,11 @@ class DepreciationsTransformer
$array = [
'id' => (int) $depreciation->id,
'name' => e($depreciation->name),
- 'months' => $depreciation->months.' '.trans('general.months'),
+ 'months' => trans_choice('general.months_plural', $depreciation->months),
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
- 'assets_count' => $depreciation->assets_count,
- 'models_count' => $depreciation->models_count,
- 'licenses_count' => $depreciation->licenses_count,
+ '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,
'created_by' => ($depreciation->adminuser) ? [
'id' => (int) $depreciation->adminuser->id,
'name'=> e($depreciation->adminuser->present()->fullName()),
diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php
index b63a5725a4..af2e902087 100644
--- a/app/Http/Transformers/LicensesTransformer.php
+++ b/app/Http/Transformers/LicensesTransformer.php
@@ -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 > 0)) ? true : false,
+ 'delete' => (Gate::allows('delete', License::class) && ($license->free_seats_count == $license->seats)) ? true : false,
];
$array += $permissions_array;
diff --git a/app/Http/Transformers/AssetMaintenancesTransformer.php b/app/Http/Transformers/MaintenancesTransformer.php
similarity index 82%
rename from app/Http/Transformers/AssetMaintenancesTransformer.php
rename to app/Http/Transformers/MaintenancesTransformer.php
index ab044260f7..c2fc8a0e14 100644
--- a/app/Http/Transformers/AssetMaintenancesTransformer.php
+++ b/app/Http/Transformers/MaintenancesTransformer.php
@@ -4,23 +4,24 @@ namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
-use App\Models\AssetMaintenance;
+use App\Models\Maintenance;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Facades\Storage;
-class AssetMaintenancesTransformer
+class MaintenancesTransformer
{
- public function transformAssetMaintenances(Collection $assetmaintenances, $total)
+ public function transformMaintenances(Collection $maintenances, $total)
{
$array = [];
- foreach ($assetmaintenances as $assetmaintenance) {
- $array[] = self::transformAssetMaintenance($assetmaintenance);
+ foreach ($maintenances as $assetmaintenance) {
+ $array[] = self::transformMaintenance($assetmaintenance);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
- public function transformAssetMaintenance(AssetMaintenance $assetmaintenance)
+ public function transformMaintenance(Maintenance $assetmaintenance)
{
$array = [
'id' => (int) $assetmaintenance->id,
@@ -33,6 +34,7 @@ class AssetMaintenancesTransformer
'created_at' => Helper::getFormattedDateObject($assetmaintenance->asset->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($assetmaintenance->asset->updated_at, 'datetime'),
] : null,
+ 'image' => ($assetmaintenance->image != '') ? Storage::disk('public')->url('maintenances/'.e($assetmaintenance->image)) : null,
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
'id' => (int) $assetmaintenance->asset->model->id,
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
@@ -48,7 +50,8 @@ class AssetMaintenancesTransformer
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
] : null,
- 'title' => ($assetmaintenance->title) ? e($assetmaintenance->title) : null,
+ 'name' => ($assetmaintenance->name) ? e($assetmaintenance->name) : null,
+ 'title' => ($assetmaintenance->name) ? e($assetmaintenance->name) : null, // legacy to not change the shape of the API
'location' => (($assetmaintenance->asset) && ($assetmaintenance->asset->location)) ? [
'id' => (int) $assetmaintenance->asset->location->id,
'name'=> e($assetmaintenance->asset->location->name),
@@ -59,7 +62,10 @@ 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'),
diff --git a/app/Http/Transformers/ProfileTransformer.php b/app/Http/Transformers/ProfileTransformer.php
new file mode 100644
index 0000000000..9b1104515c
--- /dev/null
+++ b/app/Http/Transformers/ProfileTransformer.php
@@ -0,0 +1,43 @@
+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;
+ }
+
+}
diff --git a/app/Http/Transformers/UploadedFilesTransformer.php b/app/Http/Transformers/UploadedFilesTransformer.php
index a18c9f9b65..ae6c981eda 100644
--- a/app/Http/Transformers/UploadedFilesTransformer.php
+++ b/app/Http/Transformers/UploadedFilesTransformer.php
@@ -3,10 +3,10 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
+use App\Helpers\StorageHelper;
use App\Models\Actionlog;
-use App\Models\Asset;
-use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
class UploadedFilesTransformer
@@ -26,23 +26,27 @@ 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),
- 'url' => $file_url,
+ 'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
+ 'mediatype' => StorageHelper::getMediaType($file->uploads_file_path()),
+ 'url' => $file->uploads_file_url(),
+ 'note' => ($file->note) ? e($file->note) : null,
'created_by' => ($file->adminuser) ? [
'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'] = [
@@ -53,4 +57,5 @@ class UploadedFilesTransformer
return $array;
}
-}
+
+}
\ No newline at end of file
diff --git a/app/Http/Transformers/UsersTransformer.php b/app/Http/Transformers/UsersTransformer.php
index 3bf3ee9702..fb941007ab 100644
--- a/app/Http/Transformers/UsersTransformer.php
+++ b/app/Http/Transformers/UsersTransformer.php
@@ -22,6 +22,12 @@ 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,
@@ -39,6 +45,7 @@ class UsersTransformer
'jobtitle' => ($user->jobtitle) ? e($user->jobtitle) : null,
'vip' => ($user->vip == '1') ? true : false,
'phone' => ($user->phone) ? e($user->phone) : null,
+ 'mobile' => ($user->mobile) ? e($user->mobile) : null,
'website' => ($user->website) ? e($user->website) : null,
'address' => ($user->address) ? e($user->address) : null,
'city' => ($user->city) ? e($user->city) : null,
@@ -50,11 +57,16 @@ 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,
diff --git a/app/Importer/AssetImporter.php b/app/Importer/AssetImporter.php
index ad69028055..1d03d6f95b 100644
--- a/app/Importer/AssetImporter.php
+++ b/app/Importer/AssetImporter.php
@@ -80,7 +80,16 @@ class AssetImporter extends ItemImporter
$asset_tag = Asset::autoincrement_asset();
}
- $asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
+
+
+ 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();
+ }
+
if ($asset) {
if (! $this->updating) {
$exists_error = trans('general.import_asset_tag_exists', ['asset_tag' => $asset_tag]);
diff --git a/app/Importer/CategoryImporter.php b/app/Importer/CategoryImporter.php
index 4eeb48c609..39b477c96a 100644
--- a/app/Importer/CategoryImporter.php
+++ b/app/Importer/CategoryImporter.php
@@ -66,7 +66,7 @@ class CategoryImporter extends ItemImporter
$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($this->findCsvMatch($row, 'category_type'));
+ $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;
diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php
index 0d4b8d4932..3c0ea4e264 100644
--- a/app/Importer/Importer.php
+++ b/app/Importer/Importer.php
@@ -88,6 +88,7 @@ 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',
@@ -132,7 +133,7 @@ abstract class Importer
} else {
$this->csv = Reader::createFromString($file);
}
- $this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
+ $this->tempPassword = '*** NO PASSWORD - IMPORTED VIA CSV ***';
}
// Cached Values for import lookups
diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php
index 9283e79ab9..92f44d991d 100644
--- a/app/Importer/ItemImporter.php
+++ b/app/Importer/ItemImporter.php
@@ -353,16 +353,27 @@ class ItemImporter extends Importer
* @param $user_manager string
* @return int id of company created/found
*/
- public function fetchManager($user_manager_first_name, $user_manager_last_name)
+ public function fetchManager($user_manager_username = null, $user_manager_employee_num = null, $user_manager_first_name = null, $user_manager_last_name = null)
{
- $manager = User::where('first_name', '=', $user_manager_first_name)
- ->where('last_name', '=', $user_manager_last_name)->first();
+ 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');
+ }
+
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 '.$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. ');
+
+ $this->log('No matching Manager found. If their user account is being created through this import, you should re-process this file again. ');
return null;
}
diff --git a/app/Importer/UserImporter.php b/app/Importer/UserImporter.php
index 77317b3d09..633551c24d 100644
--- a/app/Importer/UserImporter.php
+++ b/app/Importer/UserImporter.php
@@ -7,7 +7,9 @@ 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
@@ -50,6 +52,7 @@ class UserImporter extends ItemImporter
$this->item['email'] = trim($this->findCsvMatch($row, 'email'));
$this->item['gravatar'] = trim($this->findCsvMatch($row, 'gravatar'));
$this->item['phone'] = trim($this->findCsvMatch($row, 'phone_number'));
+ $this->item['mobile'] = trim($this->findCsvMatch($row, 'mobile_number'));
$this->item['website'] = trim($this->findCsvMatch($row, 'website'));
$this->item['jobtitle'] = trim($this->findCsvMatch($row, 'jobtitle'));
$this->item['address'] = trim($this->findCsvMatch($row, 'address'));
@@ -62,7 +65,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_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
+ $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['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;
@@ -80,6 +83,7 @@ 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']);
@@ -89,12 +93,25 @@ 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
@@ -110,28 +127,32 @@ class UserImporter extends ItemImporter
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords
// Issue #5408
- $this->item['password'] = bcrypt($this->tempPassword);
+ $this->item['password'] = $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) {
- $user->notify(new WelcomeNotification($data));
+
+ try {
+ $user->notify(new WelcomeNotification($user));
+ } catch (\Exception $e) {
+ Log::warning('Could not send welcome notification for user: ' . $e->getMessage());
+ }
+
}
+
}
$user = null;
$this->item = null;
@@ -140,9 +161,9 @@ class UserImporter extends ItemImporter
}
$this->logError($user, 'User');
- return;
}
+
/**
* Fetch an existing department, or create new if it doesn't exist
*
diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php
index e647bcd9e0..7e289b19e6 100644
--- a/app/Listeners/CheckoutableListener.php
+++ b/app/Listeners/CheckoutableListener.php
@@ -4,14 +4,17 @@ 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;
@@ -21,9 +24,11 @@ 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;
@@ -38,7 +43,7 @@ use Osama\LaravelTeamsNotification\TeamsNotification;
class CheckoutableListener
{
private array $skipNotificationsFor = [
- Component::class,
+// Component::class,
];
/**
@@ -69,16 +74,16 @@ class CheckoutableListener
return;
}
+ $acceptance = $this->getCheckoutAcceptance($event);
+
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($event->checkoutable);
- $shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
+ $shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress($acceptance);
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
return;
}
- $acceptance = $this->getCheckoutAcceptance($event);
-
if ($shouldSendEmailToUser || $shouldSendEmailToAlertAddress) {
$mailable = $this->getCheckoutMailType($event, $acceptance);
$notifiable = $this->getNotifiableUser($event);
@@ -144,7 +149,6 @@ class CheckoutableListener
$shouldSendEmailToUser = $this->checkoutableCategoryShouldSendEmail($event->checkoutable);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress();
$shouldSendWebhookNotification = $this->shouldSendWebhookNotification();
-
if (!$shouldSendEmailToUser && !$shouldSendEmailToAlertAddress && !$shouldSendWebhookNotification) {
return;
}
@@ -227,6 +231,7 @@ class CheckoutableListener
if ($checkedOutToType != "App\Models\User") {
return null;
}
+
if (!$event->checkoutable->requireAcceptance()) {
return null;
}
@@ -234,6 +239,13 @@ 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;
@@ -260,6 +272,9 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckinLicenseSeatNotification::class;
break;
+ case Component::class:
+ $notificationClass = CheckinComponentNotification::class;
+ break;
}
Log::debug('Notification class: '.$notificationClass);
@@ -290,6 +305,9 @@ class CheckoutableListener
case LicenseSeat::class:
$notificationClass = CheckoutLicenseSeatNotification::class;
break;
+ case Component::class:
+ $notificationClass = CheckoutComponentNotification::class;
+ break;
}
@@ -301,6 +319,7 @@ class CheckoutableListener
Asset::class => CheckoutAssetMail::class,
LicenseSeat::class => CheckoutLicenseMail::class,
Consumable::class => CheckoutConsumableMail::class,
+ Component::class => CheckoutComponentMail::class,
];
$mailable= $lookup[get_class($event->checkoutable)];
@@ -313,8 +332,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);
@@ -360,23 +379,6 @@ 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;
@@ -419,9 +421,19 @@ class CheckoutableListener
return false;
}
- private function shouldSendEmailToAlertAddress(): bool
+ private function shouldSendEmailToAlertAddress($acceptance = null): bool
{
- return Setting::getSettings() && Setting::getSettings()->admin_cc_email;
+ $setting = Setting::getSettings();
+
+ if (!$setting) {
+ return false;
+ }
+
+ if (is_null($acceptance) && !$setting->admin_cc_always) {
+ return false;
+ }
+
+ return (bool) $setting->admin_cc_email;
}
private function getFormattedAlertAddresses(): array
@@ -461,4 +473,15 @@ 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,
+ };
+ }
}
diff --git a/app/Livewire/CategoryEditForm.php b/app/Livewire/CategoryEditForm.php
index fd8bef6489..98e505c8df 100644
--- a/app/Livewire/CategoryEditForm.php
+++ b/app/Livewire/CategoryEditForm.php
@@ -6,6 +6,8 @@ use Livewire\Component;
class CategoryEditForm extends Component
{
+ public bool $alertOnResponse;
+
public $defaultEulaText;
public $eulaText;
diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php
index eef569e314..6e6a6f1847 100644
--- a/app/Livewire/Importer.php
+++ b/app/Livewire/Importer.php
@@ -203,6 +203,7 @@ 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')]),
@@ -329,8 +330,11 @@ 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'),
+ 'mobile_number' => trans('admin/users/table.mobile'),
'remote' => trans('admin/users/general.remote'),
'start_date' => trans('general.start_date'),
'state' => trans('general.state'),
@@ -507,6 +511,13 @@ class Importer extends Component
'telephone',
'tel.',
],
+ 'mobile_number' =>
+ [
+ 'mobile',
+ 'mobile number',
+ 'cell',
+ 'cellphone',
+ ],
'serial' =>
[
diff --git a/app/Livewire/LocationScopeCheck.php b/app/Livewire/LocationScopeCheck.php
index e176d41f0a..24beb9413f 100644
--- a/app/Livewire/LocationScopeCheck.php
+++ b/app/Livewire/LocationScopeCheck.php
@@ -10,15 +10,16 @@ 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()
diff --git a/app/Mail/CheckinComponentMail.php b/app/Mail/CheckinComponentMail.php
new file mode 100644
index 0000000000..5b62a2c4b8
--- /dev/null
+++ b/app/Mail/CheckinComponentMail.php
@@ -0,0 +1,71 @@
+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
+ */
+ public function attachments(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Mail/CheckoutAcceptanceResponseMail.php b/app/Mail/CheckoutAcceptanceResponseMail.php
new file mode 100644
index 0000000000..fba6dd3de2
--- /dev/null
+++ b/app/Mail/CheckoutAcceptanceResponseMail.php
@@ -0,0 +1,79 @@
+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
+ */
+ public function attachments(): array
+ {
+ return [];
+ }
+
+ private function introduction(): string
+ {
+ return $this->wasAccepted
+ ? trans('mail.following_accepted')
+ : trans('mail.following_declined');
+ }
+}
diff --git a/app/Mail/CheckoutAssetMail.php b/app/Mail/CheckoutAssetMail.php
index 7ac20861ed..44e0c06100 100644
--- a/app/Mail/CheckoutAssetMail.php
+++ b/app/Mail/CheckoutAssetMail.php
@@ -24,16 +24,25 @@ class CheckoutAssetMail extends Mailable
/**
* Create a new message instance.
+ * @throws \Exception
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
$this->note = $note;
- $this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
+ $this->target = $checkedOutTo;
+
+ // Location is a target option, but there are no emails currently associated with locations.
+ if($this->target instanceof User){
+ $this->target = $this->target->present()?->fullName();
+ }
+ else if($this->target instanceof Asset){
+ $this->target = $this->target->assignedto?->present()?->fullName();
+ }
$this->last_checkout = '';
$this->expected_checkin = '';
diff --git a/app/Mail/CheckoutComponentMail.php b/app/Mail/CheckoutComponentMail.php
new file mode 100644
index 0000000000..e914d14196
--- /dev/null
+++ b/app/Mail/CheckoutComponentMail.php
@@ -0,0 +1,82 @@
+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
+ */
+ public function attachments(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Mail/CheckoutLicenseMail.php b/app/Mail/CheckoutLicenseMail.php
index 9462c6c332..e0701413e7 100644
--- a/app/Mail/CheckoutLicenseMail.php
+++ b/app/Mail/CheckoutLicenseMail.php
@@ -2,6 +2,7 @@
namespace App\Mail;
+use App\Models\Asset;
use App\Models\LicenseSeat;
use App\Models\Setting;
use App\Models\User;
@@ -25,9 +26,16 @@ class CheckoutLicenseMail extends Mailable
$this->item = $licenseSeat;
$this->admin = $checkedOutBy;
$this->note = $note;
- $this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
+ $this->target = $checkedOutTo;
+
+ if($this->target instanceof User){
+ $this->target = $this->target->present()?->fullName();
+ }
+ elseif($this->target instanceof Asset){
+ $this->target = $this->target->assignedto?->present()?->fullName();
+ }
}
/**
diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php
index 039f8692f6..18c0b853c6 100755
--- a/app/Models/Accessory.php
+++ b/app/Models/Accessory.php
@@ -4,6 +4,7 @@ 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;
@@ -14,7 +15,7 @@ use Watson\Validating\ValidatingTrait;
/**
* Model for Accessories.
*
- * @version v1.0
+ * @version v1.0
*/
class Accessory extends SnipeModel
{
@@ -22,6 +23,7 @@ class Accessory extends SnipeModel
protected $presenter = \App\Presenters\AccessoryPresenter::class;
use CompanyableTrait;
+ use HasUploads;
use Loggable, Presentable;
use SoftDeletes;
@@ -54,8 +56,8 @@ class Accessory extends SnipeModel
];
/**
- * Accessory validation rules
- */
+ * Accessory validation rules
+ */
public $rules = [
'name' => 'required|min:3|max:255',
'qty' => 'required|integer|min:1',
@@ -69,12 +71,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;
@@ -102,29 +104,11 @@ class Accessory extends SnipeModel
];
-
- /**
- * Establishes the accessories -> action logs -> uploads relationship
- *
- * @author A. Gianotto
- * @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] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
@@ -137,7 +121,7 @@ class Accessory extends SnipeModel
* Sets the requestable attribute on the accessory
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return void
*/
public function setRequestableAttribute($value)
@@ -152,7 +136,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> company relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -164,7 +148,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> location relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -176,7 +160,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> category relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -188,7 +172,7 @@ class Accessory extends SnipeModel
* Returns the action logs associated with the accessory
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
@@ -217,8 +201,8 @@ class Accessory extends SnipeModel
*
* It's super-mega-assy, but it's the best I could do for now.
*
- * @author A. Gianotto
- * @since v5.0.0
+ * @author A. Gianotto
+ * @since v5.0.0
*
* @see \App\Http\Controllers\Api\AccessoriesController\checkedout()
*/
@@ -235,7 +219,7 @@ class Accessory extends SnipeModel
* presenter or service provider
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string
*/
public function getImageUrl()
@@ -251,7 +235,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> users relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function checkouts()
@@ -264,7 +248,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> admin user relationship
*
* @author A. Gianotto
- * @since [v7.0.13]
+ * @since [v7.0.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -276,7 +260,7 @@ class Accessory extends SnipeModel
* Checks whether or not the accessory has users
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return int
*/
public function hasUsers()
@@ -290,7 +274,7 @@ class Accessory extends SnipeModel
* Establishes the accessory -> manufacturer relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -303,12 +287,12 @@ class Accessory extends SnipeModel
* accessory based on the category it belongs to.
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return bool
*/
public function checkin_email()
{
- return $this->category->checkin_email;
+ return $this->category?->checkin_email;
}
/**
@@ -316,7 +300,7 @@ class Accessory extends SnipeModel
* accept it via email.
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return bool
*/
public function requireAcceptance()
@@ -329,7 +313,7 @@ class Accessory extends SnipeModel
* checks for a settings level EULA
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string
*/
public function getEula()
@@ -349,7 +333,7 @@ class Accessory extends SnipeModel
* Check how many items within an accessory are checked out
*
* @author [A. Gianotto] []
- * @since [v5.0]
+ * @since [v5.0]
* @return int
*/
public function numCheckedOut()
@@ -366,7 +350,7 @@ class Accessory extends SnipeModel
* bad things happen.
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return int
*/
public function numRemaining()
@@ -381,8 +365,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)
{
@@ -408,8 +392,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
- * @since v6.3.4
- * @param $value
+ * @since v6.3.4
+ * @param $value
* @return void
*/
public function setQtyAttribute($value)
@@ -426,7 +410,6 @@ class Accessory extends SnipeModel
/**
* Query builder scope to order on created_by name
- *
*/
public function scopeOrderByCreatedByName($query, $order)
{
@@ -434,68 +417,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);
diff --git a/app/Models/AccessoryCheckout.php b/app/Models/AccessoryCheckout.php
index 61ffcd08e5..9f49354389 100755
--- a/app/Models/AccessoryCheckout.php
+++ b/app/Models/AccessoryCheckout.php
@@ -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] []
- * @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,50 +127,64 @@ 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);
- })->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);
+ $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);
+ }
+ }
+ );
return $query;
}
diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php
index dd86ae25c6..bc1b5c72a2 100755
--- a/app/Models/Actionlog.php
+++ b/app/Models/Actionlog.php
@@ -7,12 +7,13 @@ use App\Presenters\Presentable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Support\Str;
/**
* Model for the Actionlog (the table that keeps a historical log of
* checkouts, checkins, and updates).
*
- * @version v1.0
+ * @version v1.0
*/
class Actionlog extends SnipeModel
{
@@ -69,11 +70,13 @@ class Actionlog extends SnipeModel
*/
protected $searchableRelations = [
'company' => ['name'],
- 'adminuser' => ['first_name','last_name','username', 'email'],
- 'user' => ['first_name','last_name','username', 'email'],
+ 'adminuser' => ['first_name','last_name','username', 'email', 'employee_num'],
+ 'user' => ['first_name','last_name','username', 'email', 'employee_num'],
'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'],
@@ -96,24 +99,31 @@ class Actionlog extends SnipeModel
* Override from Builder to automatically add the company
*
* @author [A. Gianotto] []
- * @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;
+ 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;
}
- } elseif (auth()->user() && auth()->user()->company) {
- $actionlog->company_id = auth()->user()->company_id;
+
+ if ($actionlog->action_date == '') {
+ $actionlog->action_date = Carbon::now();
+ }
+
}
- });
+ );
}
@@ -121,7 +131,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> item relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function item()
@@ -133,7 +143,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> company relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -146,7 +156,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> asset relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -158,7 +168,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> license relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -170,7 +180,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> consumable relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
@@ -182,7 +192,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> consumable relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessories()
@@ -194,7 +204,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> components relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
@@ -206,7 +216,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> item type relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function itemType()
@@ -222,7 +232,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> target type relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function targetType()
@@ -239,21 +249,21 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> uploads relationship
*
* @author [A. Gianotto] []
- * @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] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function userlog()
@@ -265,20 +275,20 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> admin user relationship
*
* @author [A. Gianotto] []
- * @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] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
@@ -291,7 +301,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> target relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function target()
@@ -303,7 +313,7 @@ class Actionlog extends SnipeModel
* Establishes the actionlog -> location relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -316,7 +326,7 @@ class Actionlog extends SnipeModel
* Check if the file exists, and if it does, force a download
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string | false
*/
public function get_src($type = 'assets', $fieldname = 'filename')
@@ -334,7 +344,7 @@ class Actionlog extends SnipeModel
* Saves the log record with the action type
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return bool
*/
public function logaction($actiontype)
@@ -355,7 +365,7 @@ class Actionlog extends SnipeModel
* Calculate the number of days until the next audit
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return int
*/
public function daysUntilNextAudit($monthInterval = 12, $asset = null)
@@ -376,7 +386,7 @@ class Actionlog extends SnipeModel
if ($this->created_at > $override_default_next) {
$next_audit_days = '-'.$next_audit_days;
}
-
+
return $next_audit_days;
}
@@ -384,7 +394,7 @@ class Actionlog extends SnipeModel
* Calculate the date of the next audit
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Datetime
*/
public function calcNextAuditDate($monthInterval = 12, $asset = null)
@@ -401,24 +411,24 @@ class Actionlog extends SnipeModel
/**
* Gets action logs in chronological order, excluding uploads
*
- * @author Vincent Sposato
- * @since v1.0
+ * @author Vincent Sposato
+ * @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
- * @since v6.3.0
+ * @since v6.3.0
* @return string
*/
public function determineActionSource(): string
@@ -430,11 +440,12 @@ 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';
}
@@ -444,6 +455,61 @@ 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]);
+ }
+
+ $object = Str::snake(str_plural(str_replace("App\Models\\", '', $this->item_type)));
+
+ if ($object == 'asset_models') {
+ $object = 'models';
+ }
+
+ return route('ui.files.show', [
+ 'object_type' => $object,
+ 'id' => $this->item_id,
+ 'file_id' => $this->id,
+ ]);
+
+ }
+
+ public function uploads_file_path()
+ {
+
+ if (($this->action_type == 'accepted') || ($this->action_type == 'declined')) {
+ return 'private_uploads/eula-pdfs/'.$this->filename;
+ }
+
+ switch ($this->item_type) {
+ case Accessory::class:
+ return 'private_uploads/accessories/'.$this->filename;
+ case Asset::class:
+ return 'private_uploads/assets/'.$this->filename;
+ case AssetModel::class:
+ return 'private_uploads/models/'.$this->filename;
+ 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 Maintenance::class:
+ return 'private_uploads/maintenances/'.$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
{
@@ -454,4 +520,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);
}
-}
+}
\ No newline at end of file
diff --git a/app/Models/Asset.php b/app/Models/Asset.php
index d5567caf03..244101723f 100644
--- a/app/Models/Asset.php
+++ b/app/Models/Asset.php
@@ -7,24 +7,24 @@ use App\Exceptions\CheckoutNotAllowed;
use App\Helpers\Helper;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\Acceptable;
+use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use App\Presenters\AssetPresenter;
-use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
-use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Support\Facades\Crypt;
+use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
-use Illuminate\Database\Eloquent\Model;
/**
* Model for Assets.
*
- * @version v1.0
+ * @version v1.0
*/
class Asset extends Depreciable
{
@@ -33,6 +33,7 @@ class Asset extends Depreciable
protected $with = ['model', 'adminuser'];
use CompanyableTrait;
+ use HasUploads;
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
public const LOCATION = 'location';
@@ -44,22 +45,22 @@ class Asset extends Depreciable
/**
* 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)
{
- $this->assigned_to = null;
- $this->assigned_type = null;
- $this->accepted = null;
- $this->save();
+ $this->assigned_to = null;
+ $this->assigned_type = null;
+ $this->accepted = null;
+ $this->save();
}
/**
- * The database table used by the model.
- *
- * @var string
- */
+ * The database table used by the model.
+ *
+ * @var string
+ */
protected $table = 'assets';
/**
@@ -69,12 +70,12 @@ class Asset extends Depreciable
// protected $with = ['model'];
/**
- * 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;
protected $casts = [
@@ -111,7 +112,7 @@ class Asset extends Depreciable
'location_id' => ['nullable', 'exists:locations,id', 'fmcs_location'],
'rtd_location_id' => ['nullable', 'exists:locations,id', 'fmcs_location'],
'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'],
- 'serial' => ['nullable', 'unique_undeleted:assets,serial'],
+ 'serial' => ['nullable', 'string', 'unique_undeleted:assets,serial'],
'purchase_cost' => ['nullable', 'numeric', 'gte:0', 'max:9999999999999'],
'supplier_id' => ['nullable', 'exists:suppliers,id'],
'asset_eol_date' => ['nullable', 'date'],
@@ -122,17 +123,17 @@ class Asset extends Depreciable
'assigned_to' => ['nullable', 'integer', 'required_with:assigned_type'],
'assigned_type' => ['nullable', 'required_with:assigned_to', 'in:'.User::class.",".Location::class.",".Asset::class],
'requestable' => ['nullable', 'boolean'],
- 'assigned_user' => ['nullable', 'exists:users,id,deleted_at,NULL'],
- 'assigned_location' => ['nullable', 'exists:locations,id,deleted_at,NULL', 'fmcs_location'],
- 'assigned_asset' => ['nullable', 'exists:assets,id,deleted_at,NULL']
+ 'assigned_user' => ['integer', 'nullable', 'exists:users,id,deleted_at,NULL'],
+ 'assigned_location' => ['integer', 'nullable', 'exists:locations,id,deleted_at,NULL', 'fmcs_location'],
+ 'assigned_asset' => ['integer', 'nullable', 'exists:assets,id,deleted_at,NULL']
];
/**
- * The attributes that are mass assignable.
- *
- * @var array
- */
+ * The attributes that are mass assignable.
+ *
+ * @var array
+ */
protected $fillable = [
'asset_tag',
'assigned_to',
@@ -205,6 +206,17 @@ class Asset extends Depreciable
'model.manufacturer' => ['name'],
];
+ protected static function booted(): void
+ {
+ static::forceDeleted(function (Asset $asset) {
+ $asset->requests()->forceDelete();
+ });
+
+ static::softDeleted(function (Asset $asset) {
+ $asset->requests()->delete();
+ });
+ }
+
// To properly set the expected checkin as Y-m-d
public function setExpectedCheckinAttribute($value)
{
@@ -221,18 +233,22 @@ class Asset extends Depreciable
$customFieldValidationRules = [];
- if (($this->model) && ($this->model->fieldset)) {
+ if (($this->model) && ($this->model->fieldset)) {
- foreach ($this->model->fieldset->fields as $field) {
+ foreach ($this->model->fieldset->fields as $field) {
- if ($field->format == 'BOOLEAN'){
- $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
- }
+ // this just casts booleans that may come through as strings to an actual boolean type
+ // adding !$field->field_encrypted because when the encrypted value comes through it
+ // screws things up for the encrypted validation rules (and the encrypted string
+ // is not a valid boolean type)
+ if ($field->format == 'BOOLEAN' && !$field->field_encrypted) {
+ $this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
-
- $customFieldValidationRules += $this->model->fieldset->validation_rules();
}
+ $customFieldValidationRules += $this->model->fieldset->validation_rules();
+ }
+
return $customFieldValidationRules;
}
@@ -258,6 +274,7 @@ class Asset extends Depreciable
/**
* Returns the warranty expiration date as Carbon object
+ *
* @return \Carbon\Carbon|null
*/
public function getWarrantyExpiresAttribute()
@@ -281,7 +298,7 @@ class Asset extends Depreciable
* Establishes the asset -> company relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -296,7 +313,7 @@ class Asset extends Depreciable
* that the status is deployable
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return bool
*/
public function availableForCheckout()
@@ -307,8 +324,8 @@ class Asset extends Depreciable
// The asset status is not archived and is deployable
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
- && ($this->assetstatus->deployable == '1'))
- {
+ && ($this->assetstatus->deployable == '1')
+ ) {
return true;
}
@@ -323,14 +340,14 @@ class Asset extends Depreciable
* @todo The admin parameter is never used. Can probably be removed.
*
* @author [A. Gianotto] []
- * @param User $user
- * @param User $admin
- * @param Carbon $checkout_at
- * @param Carbon $expected_checkin
- * @param string $note
- * @param null $name
+ * @param User $user
+ * @param User $admin
+ * @param Carbon $checkout_at
+ * @param Carbon $expected_checkin
+ * @param string $note
+ * @param null $name
* @return bool
- * @since [v3.0]
+ * @since [v3.0]
* @return bool
*/
public function checkOut($target, $admin = null, $checkout_at = null, $expected_checkin = null, $note = null, $name = null, $location = null)
@@ -391,7 +408,7 @@ class Asset extends Depreciable
* Sets the detailedNameAttribute
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string
*/
public function getDetailedNameAttribute()
@@ -409,7 +426,7 @@ class Asset extends Depreciable
* Pulls in the validation rules
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return array
*/
public function validationRules()
@@ -417,15 +434,38 @@ class Asset extends Depreciable
return $this->rules;
}
- public function customFieldsForCheckinCheckout($checkin_checkout) {
+ public function customFieldsForCheckinCheckout($checkin_checkout)
+ {
// Check to see if any of the custom fields were included on the form and if they have any values
if (($this->model) && ($this->model->fieldset) && ($this->model->fieldset->fields)) {
+
foreach ($this->model->fieldset->fields as $field) {
- if (($field->{$checkin_checkout} == 1) && (request()->has($field->db_column))){
- $this->{$field->db_column} = request()->get($field->db_column);
+
+ if (($field->{$checkin_checkout} == 1) && (request()->has($field->db_column))) {
+
+ if ($field->field_encrypted == '1') {
+
+ if (Gate::allows('assets.view.encrypted_custom_fields')) {
+ if (is_array(request()->input($field->db_column))) {
+ $this->{$field->db_column} = Crypt::encrypt(implode(', ', request()->input($field->db_column)));
+ } else {
+ $this->{$field->db_column} = Crypt::encrypt(request()->get($field->db_column));
+ }
+ }
+
+ } else {
+
+ if (is_array(request()->input($field->db_column))) {
+ $this->{$field->db_column} = implode(', ', request()->input($field->db_column));
+ } else {
+ $this->{$field->db_column} = request()->input($field->db_column);
+ }
+
+ }
}
}
}
+
}
@@ -433,12 +473,12 @@ class Asset extends Depreciable
* Establishes the asset -> depreciation relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function depreciation()
{
- return $this->hasOneThrough(\App\Models\Depreciation::class,\App\Models\AssetModel::class,'id','id','model_id','depreciation_id');
+ return $this->hasOneThrough(\App\Models\Depreciation::class, \App\Models\AssetModel::class, 'id', 'id', 'model_id', 'depreciation_id');
}
@@ -446,7 +486,7 @@ class Asset extends Depreciable
* Get components assigned to this asset
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
@@ -461,7 +501,7 @@ class Asset extends Depreciable
* @todo Is this still needed?
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function get_depreciation()
@@ -472,51 +512,35 @@ class Asset extends Depreciable
}
- /**
- * Get uploads for this asset
- *
- * @author [A. Gianotto] []
- * @since [v4.0]
- * @return \Illuminate\Database\Eloquent\Relations\Relation
- */
- public function uploads()
- {
- return $this->hasMany('\App\Models\Actionlog', 'item_id')
- ->where('item_type', '=', Asset::class)
- ->where('action_type', '=', 'uploaded')
- ->whereNotNull('filename')
- ->orderBy('created_at', 'desc');
- }
-
/**
* Determines whether the asset is checked out to a user
*
- * Even though we allow allow for checkout to things beyond users
+ * Even though we allow for checkout to things beyond users
* this method is an easy way of seeing if we are checked out to a user.
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
*/
public function checkedOutToUser(): bool
{
- return $this->assignedType() === self::USER;
+ return $this->assignedType() === self::USER;
}
public function checkedOutToLocation(): bool
{
- return $this->assignedType() === self::LOCATION;
+ return $this->assignedType() === self::LOCATION;
}
public function checkedOutToAsset(): bool
{
- return $this->assignedType() === self::ASSET;
+ return $this->assignedType() === self::ASSET;
}
/**
* Get the target this asset is checked out to
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedTo()
@@ -530,7 +554,7 @@ class Asset extends Depreciable
* Sigh.
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedAssets()
@@ -542,7 +566,7 @@ class Asset extends Depreciable
* Establishes the accessory -> asset assignment relationship
*
* @author A. Gianotto
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedAccessories()
@@ -557,7 +581,7 @@ class Asset extends Depreciable
* @todo Refactor this if possible. It's awful.
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \ArrayObject
*/
public function assetLoc($iterations = 1, $first_asset = null)
@@ -598,7 +622,7 @@ class Asset extends Depreciable
* Gets the lowercased name of the type of target the asset is assigned to
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return string
*/
public function assignedType()
@@ -610,10 +634,11 @@ class Asset extends Depreciable
/**
* This is annoying, but because we don't say "assets" in our route names, we have to make an exception here
+ *
* @todo - normalize the route names - API endpoint URLS can stay the same
*
* @author [A. Gianotto] []
- * @since [v6.1.0]
+ * @since [v6.1.0]
* @return string
*/
public function targetShowRoute()
@@ -632,7 +657,7 @@ class Asset extends Depreciable
* Get the asset's location based on default RTD location
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function defaultLoc()
@@ -647,7 +672,7 @@ class Asset extends Depreciable
* and if not, check for an image uploaded to the asset model.
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return string | false
*/
public function getImageUrl()
@@ -668,22 +693,22 @@ class Asset extends Depreciable
* Get the asset's logs
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
{
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
- ->where('item_type', '=', self::class)
- ->orderBy('created_at', 'desc')
- ->withTrashed();
+ ->where('item_type', '=', self::class)
+ ->orderBy('created_at', 'desc')
+ ->withTrashed();
}
/**
* Get the list of checkouts for this asset
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function checkouts()
@@ -698,7 +723,7 @@ class Asset extends Depreciable
* Get the list of audits for this asset
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function audits()
@@ -712,7 +737,7 @@ class Asset extends Depreciable
* Get the list of checkins for this asset
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function checkins()
@@ -727,7 +752,7 @@ class Asset extends Depreciable
* Get the asset's user requests
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function userRequests()
@@ -742,21 +767,21 @@ class Asset extends Depreciable
/**
* Get maintenances for this asset
*
- * @author Vincent Sposato
- * @since 1.0
+ * @author Vincent Sposato
+ * @since 1.0
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
- public function assetmaintenances()
+ public function maintenances()
{
- return $this->hasMany(\App\Models\AssetMaintenance::class, 'asset_id')
- ->orderBy('created_at', 'desc');
+ return $this->hasMany(\App\Models\Maintenance::class, 'asset_id')
+ ->orderBy('created_at', 'desc');
}
/**
* Get user who created the item
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -770,7 +795,7 @@ class Asset extends Depreciable
* Establishes the asset -> status relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetstatus()
@@ -782,7 +807,7 @@ class Asset extends Depreciable
* Establishes the asset -> model relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function model()
@@ -793,9 +818,9 @@ class Asset extends Depreciable
/**
* Return the assets with a warranty expiring within x days
*
- * @param $days
+ * @param $days
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return mixed
*/
public static function getExpiringWarrantee($days = 30)
@@ -807,9 +832,11 @@ class Asset extends Depreciable
->whereNotNull('purchase_date')
->whereNull('deleted_at')
->NotArchived()
- ->whereRaw('DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) <= DATE_ADD(NOW(), INTERVAL '
+ ->whereRaw(
+ 'DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) <= DATE_ADD(NOW(), INTERVAL '
. $days
- . ' DAY) AND DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) > NOW()')
+ . ' DAY) AND DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) > NOW()'
+ )
->orderByRaw('DATE_ADD(`purchase_date`,INTERVAL `warranty_months` MONTH)')
->get();
}
@@ -819,7 +846,7 @@ class Asset extends Depreciable
* Establishes the asset -> assigned licenses relationship
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -831,7 +858,7 @@ class Asset extends Depreciable
* Establishes the asset -> license seats relationship
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenseseats()
@@ -843,7 +870,7 @@ class Asset extends Depreciable
* Establishes the asset -> aupplier relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
@@ -855,7 +882,7 @@ class Asset extends Depreciable
* Establishes the asset -> location relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -868,7 +895,7 @@ class Asset extends Depreciable
* Get the next autoincremented asset tag
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return string | false
*/
public static function autoincrement_asset(int $additional_increment = 0)
@@ -894,7 +921,7 @@ class Asset extends Depreciable
* We'll add the zerofill and prefixes on the fly as we generate the number.
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return int
*/
public static function nextAutoIncrement($assets)
@@ -905,12 +932,10 @@ class Asset extends Depreciable
foreach ($assets as $asset) {
$results = preg_match("/\d+$/", $asset['asset_tag'], $matches);
- if ($results)
- {
+ if ($results) {
$number = $matches[0];
- if ($number > $max)
- {
+ if ($number > $max) {
$max = $number;
}
}
@@ -927,7 +952,7 @@ class Asset extends Depreciable
* We'll add the zerofill and prefixes on the fly as we generate the number.
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return string
*/
public static function zerofill($num, $zerofill = 3)
@@ -940,7 +965,7 @@ class Asset extends Depreciable
* asset model category
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return bool
*/
public function checkin_email()
@@ -954,7 +979,7 @@ class Asset extends Depreciable
* Determine whether this asset requires acceptance by the assigned user
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return bool
*/
public function requireAcceptance()
@@ -971,7 +996,7 @@ class Asset extends Depreciable
* Determine whether this asset's next audit date is before the last audit date
*
* @return bool
- * @since [v6.4.1]
+ * @since [v6.4.1]
* @author [A. Gianotto] []
* */
public function checkInvalidNextAuditDate()
@@ -999,7 +1024,7 @@ class Asset extends Depreciable
* checks for a settings level EULA
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return string | false
*/
public function getEula()
@@ -1018,7 +1043,8 @@ class Asset extends Depreciable
return false;
}
- public function getComponentCost(){
+ public function getComponentCost()
+ {
$cost = 0;
foreach($this->components as $component) {
$cost += $component->pivot->assigned_qty*$component->purchase_cost;
@@ -1038,7 +1064,7 @@ class Asset extends Depreciable
* This is kind of dumb and confusing, since we already cast it that way AND it's a date field
* in the database, but here we are.
*
- * @param $value
+ * @param $value
* @return void
*/
@@ -1088,7 +1114,7 @@ class Asset extends Depreciable
*
* This will also correctly parse a 1/0 if "true"/"false" is passed.
*
- * @param $value
+ * @param $value
* @return void
*/
@@ -1102,16 +1128,16 @@ class Asset extends Depreciable
/**
- * -----------------------------------------------
- * BEGIN QUERY SCOPES
- * -----------------------------------------------
- **/
+ * -----------------------------------------------
+ * BEGIN QUERY SCOPES
+ * -----------------------------------------------
+ **/
/**
* 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)
@@ -1120,31 +1146,38 @@ class Asset extends Depreciable
/**
* Assigned user
*/
- $query = $query->leftJoin('users as assets_users', function ($leftJoin) {
- $leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', User::class);
- });
+ $query = $query->leftJoin(
+ 'users as assets_users', function ($leftJoin) {
+ $leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', User::class);
+ }
+ );
foreach ($terms as $term) {
$query = $query
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
+ ->orWhere('assets_users.jobtitle', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
- ->orWhereMultipleColumns([
+ ->orWhereMultipleColumns(
+ [
'assets_users.first_name',
'assets_users.last_name',
- ], $term);
+ ], $term
+ );
}
/**
* Assigned location
*/
- $query = $query->leftJoin('locations as assets_locations', function ($leftJoin) {
- $leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', Location::class);
- });
+ $query = $query->leftJoin(
+ 'locations as assets_locations', function ($leftJoin) {
+ $leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', Location::class);
+ }
+ );
foreach ($terms as $term) {
@@ -1154,10 +1187,12 @@ class Asset extends Depreciable
/**
* Assigned assets
*/
- $query = $query->leftJoin('assets as assigned_assets', function ($leftJoin) {
- $leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', self::class);
- });
+ $query = $query->leftJoin(
+ 'assets as assigned_assets', function ($leftJoin) {
+ $leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', self::class);
+ }
+ );
foreach ($terms as $term) {
$query = $query->orWhere('assigned_assets.name', 'LIKE', '%'.$term.'%');
@@ -1169,12 +1204,12 @@ class Asset extends Depreciable
/**
- * Query builder scope for hardware
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ * Query builder scope for hardware
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeHardware($query)
{
@@ -1182,101 +1217,121 @@ class Asset extends Depreciable
}
/**
- * Query builder scope for pending assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ * Query builder scope for pending assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopePending($query)
{
- return $query->whereHas('assetstatus', function ($query) {
- $query->where('deployable', '=', 0)
- ->where('pending', '=', 1)
- ->where('archived', '=', 0);
- });
+ return $query->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('deployable', '=', 0)
+ ->where('pending', '=', 1)
+ ->where('archived', '=', 0);
+ }
+ );
}
/**
- * Query builder scope for searching location
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ * Query builder scope for searching location
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeAssetsByLocation($query, $location)
{
- return $query->where(function ($query) use ($location) {
- $query->whereHas('assignedTo', function ($query) use ($location) {
- $query->where([
- ['users.location_id', '=', $location->id],
- ['assets.assigned_type', '=', User::class],
- ])->orWhere([
- ['locations.id', '=', $location->id],
- ['assets.assigned_type', '=', Location::class],
- ])->orWhere([
- ['assets.rtd_location_id', '=', $location->id],
- ['assets.assigned_type', '=', self::class],
- ]);
- })->orWhere(function ($query) use ($location) {
- $query->where('assets.rtd_location_id', '=', $location->id);
- $query->whereNull('assets.assigned_to');
- });
- });
+ return $query->where(
+ function ($query) use ($location) {
+ $query->whereHas(
+ 'assignedTo', function ($query) use ($location) {
+ $query->where(
+ [
+ ['users.location_id', '=', $location->id],
+ ['assets.assigned_type', '=', User::class],
+ ]
+ )->orWhere(
+ [
+ ['locations.id', '=', $location->id],
+ ['assets.assigned_type', '=', Location::class],
+ ]
+ )->orWhere(
+ [
+ ['assets.rtd_location_id', '=', $location->id],
+ ['assets.assigned_type', '=', self::class],
+ ]
+ );
+ }
+ )->orWhere(
+ function ($query) use ($location) {
+ $query->where('assets.rtd_location_id', '=', $location->id);
+ $query->whereNull('assets.assigned_to');
+ }
+ );
+ }
+ );
}
/**
- * Query builder scope for RTD assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ * Query builder scope for RTD assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeRTD($query)
{
return $query->whereNull('assets.assigned_to')
- ->whereHas('assetstatus', function ($query) {
- $query->where('deployable', '=', 1)
- ->where('pending', '=', 0)
- ->where('archived', '=', 0);
- });
+ ->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('deployable', '=', 1)
+ ->where('pending', '=', 0)
+ ->where('archived', '=', 0);
+ }
+ );
}
- /**
- * Query builder scope for Undeployable assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope for Undeployable assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeUndeployable($query)
{
- return $query->whereHas('assetstatus', function ($query) {
- $query->where('deployable', '=', 0)
- ->where('pending', '=', 0)
- ->where('archived', '=', 0);
- });
+ return $query->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('deployable', '=', 0)
+ ->where('pending', '=', 0)
+ ->where('archived', '=', 0);
+ }
+ );
}
/**
* Query builder scope for non-Archived assets
*
- * @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 scopeNotArchived($query)
{
- return $query->whereHas('assetstatus', function ($query) {
- $query->where('archived', '=', 0);
- });
+ return $query->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('archived', '=', 0);
+ }
+ );
}
/**
@@ -1296,8 +1351,8 @@ class Asset extends Depreciable
* now = May 4, 2019
*
* @author A. Gianotto
- * @since v4.6.16
- * @param Setting $settings
+ * @since v4.6.16
+ * @param Setting $settings
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -1322,8 +1377,8 @@ class Asset extends Depreciable
* for an upcoming API call for retrieving a report on overdue assets.
*
* @author A. Gianotto
- * @since v4.6.16
- * @param Setting $settings
+ * @since v4.6.16
+ * @param Setting $settings
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -1344,8 +1399,8 @@ class Asset extends Depreciable
* for an upcoming API call for retrieving a report on assets that will need to be audited.
*
* @author A. Gianotto
- * @since v4.6.16
- * @param Setting $settings
+ * @since v4.6.16
+ * @param Setting $settings
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -1353,11 +1408,15 @@ class Asset extends Depreciable
public function scopeDueOrOverdueForAudit($query, $settings)
{
- return $query->where(function ($query) {
- $query->OverdueForAudit();
- })->orWhere(function ($query) use ($settings) {
- $query->DueForAudit($settings);
- });
+ return $query->where(
+ function ($query) {
+ $query->OverdueForAudit();
+ }
+ )->orWhere(
+ function ($query) use ($settings) {
+ $query->DueForAudit($settings);
+ }
+ );
}
@@ -1366,7 +1425,7 @@ class Asset extends Depreciable
* and settings.audit_warning_days. It checks to see if assets.expected_checkin is now
*
* @author A. Gianotto
- * @since v6.4.0
+ * @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
@@ -1387,7 +1446,7 @@ class Asset extends Depreciable
* Query builder scope for Assets that are overdue for checkin OR overdue
*
* @author A. Gianotto
- * @since v6.4.0
+ * @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOverdueForCheckin($query)
@@ -1403,16 +1462,20 @@ class Asset extends Depreciable
* Query builder scope for Assets that are due for checkin OR overdue
*
* @author A. Gianotto
- * @since v6.4.0
+ * @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeDueOrOverdueForCheckin($query, $settings)
{
- return $query->where(function ($query) {
- $query->OverdueForCheckin();
- })->orWhere(function ($query) use ($settings) {
- $query->DueForCheckin($settings);
- });
+ return $query->where(
+ function ($query) {
+ $query->OverdueForCheckin();
+ }
+ )->orWhere(
+ function ($query) use ($settings) {
+ $query->DueForCheckin($settings);
+ }
+ );
}
@@ -1423,7 +1486,7 @@ class Asset extends Depreciable
* has chosen to not display archived assets in their regular lists
* and views, it will return the correct number.
*
- * @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
*/
@@ -1432,115 +1495,123 @@ class Asset extends Depreciable
{
if (Setting::getSettings()->show_archived_in_list!=1) {
- return $query->whereHas('assetstatus', function ($query) {
- $query->where('archived', '=', 0);
- });
+ return $query->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('archived', '=', 0);
+ }
+ );
} else {
return $query;
}
}
- /**
- * Query builder scope for Archived assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope for Archived assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeArchived($query)
{
- return $query->whereHas('assetstatus', function ($query) {
- $query->where('deployable', '=', 0)
- ->where('pending', '=', 0)
- ->where('archived', '=', 1);
- });
+ return $query->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where('deployable', '=', 0)
+ ->where('pending', '=', 0)
+ ->where('archived', '=', 1);
+ }
+ );
}
- /**
- * Query builder scope for Deployed assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope for Deployed assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeDeployed($query)
{
return $query->where('assigned_to', '>', '0');
}
- /**
- * Query builder scope for Requestable assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope for Requestable assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeRequestableAssets($query): Builder
{
$table = $query->getModel()->getTable();
return Company::scopeCompanyables($query->where($table.'.requestable', '=', 1))
- ->whereHas('assetstatus', function ($query) {
- $query->where(function ($query) {
- $query->where('deployable', '=', 1)
- ->where('archived', '=', 0); // you definitely can't request something that's archived
- })->orWhere('pending', '=', 1); // we've decided that even though an asset may be 'pending', you can still request it
- });
+ ->whereHas(
+ 'assetstatus', function ($query) {
+ $query->where(
+ function ($query) {
+ $query->where('deployable', '=', 1)
+ ->where('archived', '=', 0); // you definitely can't request something that's archived
+ }
+ )->orWhere('pending', '=', 1); // we've decided that even though an asset may be 'pending', you can still request it
+ }
+ );
}
/**
- * scopeInModelList
- * Get all assets in the provided listing of model ids
- *
- * @param $query
- * @param array $modelIdListing
- *
- * @return mixed
- * @author Vincent Sposato
- * @version v1.0
- */
+ * scopeInModelList
+ * Get all assets in the provided listing of model ids
+ *
+ * @param $query
+ * @param array $modelIdListing
+ *
+ * @return mixed
+ * @author Vincent Sposato
+ * @version v1.0
+ */
public function scopeInModelList($query, array $modelIdListing)
{
return $query->whereIn('assets.model_id', $modelIdListing);
}
- /**
- * Query builder scope to get not-yet-accepted assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope to get not-yet-accepted assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeNotYetAccepted($query)
{
return $query->where('accepted', '=', 'pending');
}
- /**
- * Query builder scope to get rejected assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope to get rejected assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeRejected($query)
{
return $query->where('accepted', '=', 'rejected');
}
- /**
- * Query builder scope to get accepted assets
- *
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- *
- * @return \Illuminate\Database\Query\Builder Modified query builder
- */
+ /**
+ * Query builder scope to get accepted assets
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeAccepted($query)
{
return $query->where('accepted', '=', 'accepted');
@@ -1549,8 +1620,8 @@ class Asset extends Depreciable
/**
* Query builder scope to search on text for complex Bootstrap Tables API.
*
- * @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
*/
@@ -1558,69 +1629,99 @@ class Asset extends Depreciable
{
$search = explode(' OR ', $search);
- return $query->leftJoin('users as assets_users', function ($leftJoin) {
- $leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', User::class);
- })->leftJoin('locations as assets_locations', function ($leftJoin) {
- $leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', Location::class);
- })->leftJoin('assets as assigned_assets', function ($leftJoin) {
- $leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', self::class);
- })->where(function ($query) use ($search) {
- foreach ($search as $search) {
- $query->whereHas('model', function ($query) use ($search) {
- $query->whereHas('category', function ($query) use ($search) {
- $query->where(function ($query) use ($search) {
- $query->where('categories.name', 'LIKE', '%'.$search.'%')
- ->orWhere('models.name', 'LIKE', '%'.$search.'%')
- ->orWhere('models.model_number', 'LIKE', '%'.$search.'%');
- });
- });
- })->orWhereHas('model', function ($query) use ($search) {
- $query->whereHas('manufacturer', function ($query) use ($search) {
- $query->where(function ($query) use ($search) {
- $query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
- });
- });
- })->orWhere(function ($query) use ($search) {
- $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
- ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
- ->orWhereMultipleColumns([
- 'assets_users.first_name',
- 'assets_users.last_name',
- ], $search)
- ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
- ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
- ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
- })->orWhere('assets.name', 'LIKE', '%'.$search.'%')
- ->orWhere('assets.asset_tag', 'LIKE', '%'.$search.'%')
- ->orWhere('assets.serial', 'LIKE', '%'.$search.'%')
- ->orWhere('assets.order_number', 'LIKE', '%'.$search.'%')
- ->orWhere('assets.notes', 'LIKE', '%'.$search.'%');
+ return $query->leftJoin(
+ 'users as assets_users', function ($leftJoin) {
+ $leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', User::class);
}
+ )->leftJoin(
+ 'locations as assets_locations', function ($leftJoin) {
+ $leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', Location::class);
+ }
+ )->leftJoin(
+ 'assets as assigned_assets', function ($leftJoin) {
+ $leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', self::class);
+ }
+ )->where(
+ function ($query) use ($search) {
+ foreach ($search as $search) {
+ $query->whereHas(
+ 'model', function ($query) use ($search) {
+ $query->whereHas(
+ 'category', function ($query) use ($search) {
+ $query->where(
+ function ($query) use ($search) {
+ $query->where('categories.name', 'LIKE', '%'.$search.'%')
+ ->orWhere('models.name', 'LIKE', '%'.$search.'%')
+ ->orWhere('models.model_number', 'LIKE', '%'.$search.'%');
+ }
+ );
+ }
+ );
+ }
+ )->orWhereHas(
+ 'model', function ($query) use ($search) {
+ $query->whereHas(
+ 'manufacturer', function ($query) use ($search) {
+ $query->where(
+ function ($query) use ($search) {
+ $query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
+ }
+ );
+ }
+ );
+ }
+ )->orWhere(
+ function ($query) use ($search) {
+ $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets_users.jobtitle', 'LIKE', '%'.$search.'%')
+ ->orWhereMultipleColumns(
+ [
+ 'assets_users.first_name',
+ 'assets_users.last_name',
+ 'assets_users.jobtitle',
+ ], $search
+ )
+ ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
+ ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
+ }
+ )->orWhere('assets.name', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets.asset_tag', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets.serial', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets.order_number', 'LIKE', '%'.$search.'%')
+ ->orWhere('assets.notes', 'LIKE', '%'.$search.'%');
+ }
- })->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
+ }
+ )->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
/**
* Query builder scope to search the department ID of users assigned to assets
*
* @author [A. Gianotto] []
- * @since [v5.0]
+ * @since [v5.0]
* @return string | false
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeCheckedOutToTargetInDepartment($query, $search)
{
- return $query->leftJoin('users as assets_dept_users', function ($leftJoin) {
- $leftJoin->on('assets_dept_users.id', '=', 'assets.assigned_to')
- ->where('assets.assigned_type', '=', User::class);
- })->where(function ($query) use ($search) {
+ return $query->leftJoin(
+ 'users as assets_dept_users', function ($leftJoin) {
+ $leftJoin->on('assets_dept_users.id', '=', 'assets.assigned_to')
+ ->where('assets.assigned_type', '=', User::class);
+ }
+ )->where(
+ function ($query) use ($search) {
$query->whereIn('assets_dept_users.department_id', $search);
- })->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
+ }
+ )->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
@@ -1628,189 +1729,229 @@ class Asset extends Depreciable
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
- * @param \Illuminate\Database\Query\Builder $query Query builder instance
- * @param text $filter JSON array of search keys and terms
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
- return $query->where(function ($query) use ($filter) {
- foreach ($filter as $key => $search_val) {
+ return $query->where(
+ function ($query) use ($filter) {
+ foreach ($filter as $key => $search_val) {
+
+ $fieldname = str_replace('custom_fields.', '', $key);
+
+ if ($fieldname == 'asset_tag') {
+ $query->where('assets.asset_tag', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'name') {
+ $query->where('assets.name', 'LIKE', '%'.$search_val.'%');
+ }
+
+
+ if ($fieldname =='serial') {
+ $query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'purchase_date') {
+ $query->where('assets.purchase_date', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'purchase_cost') {
+ $query->where('assets.purchase_cost', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'notes') {
+ $query->where('assets.notes', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'order_number') {
+ $query->where('assets.order_number', 'LIKE', '%'.$search_val.'%');
+ }
+
+ if ($fieldname == 'status_label') {
+ $query->whereHas(
+ 'assetstatus', function ($query) use ($search_val) {
+ $query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+
+ if ($fieldname == 'location') {
+ $query->whereHas(
+ 'location', function ($query) use ($search_val) {
+ $query->where('locations.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+
+ if ($fieldname == 'rtd_location') {
+ $query->whereHas(
+ 'defaultLoc', function ($query) use ($search_val) {
+ $query->where('locations.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+
+ if ($fieldname =='assigned_to') {
+ $query->whereHasMorph(
+ 'assignedTo', [User::class], function ($query) use ($search_val) {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
+ ->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+
+
+ if ($fieldname == 'manufacturer') {
+ $query->whereHas(
+ 'model', function ($query) use ($search_val) {
+ $query->whereHas(
+ 'manufacturer', function ($query) use ($search_val) {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+ );
+ }
+
+ if ($fieldname == 'category') {
+ $query->whereHas(
+ 'model', function ($query) use ($search_val) {
+ $query->whereHas(
+ 'category', function ($query) use ($search_val) {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->where('categories.name', 'LIKE', '%'.$search_val.'%')
+ ->orWhere('models.name', 'LIKE', '%'.$search_val.'%')
+ ->orWhere('models.model_number', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+ );
+ }
+
+ if ($fieldname == 'model') {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->whereHas(
+ 'model', function ($query) use ($search_val) {
+ $query->where('models.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+
+ if ($fieldname == 'model_number') {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->whereHas(
+ 'model', function ($query) use ($search_val) {
+ $query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+
+
+ if ($fieldname == 'company') {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->whereHas(
+ 'company', function ($query) use ($search_val) {
+ $query->where('companies.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+
+ if ($fieldname == 'supplier') {
+ $query->where(
+ function ($query) use ($search_val) {
+ $query->whereHas(
+ 'supplier', function ($query) use ($search_val) {
+ $query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
+ }
+ );
+ }
+ );
+ }
+
+
+ /**
+ * THIS CLUNKY BIT IS VERY IMPORTANT
+ *
+ * Although inelegant, this section matters a lot when querying against fields that do not
+ * exist on the asset table. There's probably a better way to do this moving forward, for
+ * example using the Schema:: methods to determine whether or not a column actually exists,
+ * or even just using the $searchableRelations variable earlier in this file.
+ *
+ * In short, this set of statements tells the query builder to ONLY query against an
+ * actual field that's being passed if it doesn't meet known relational fields. This
+ * allows us to query custom fields directly in the assets table
+ * (regardless of their name) and *skip* any fields that we already know can only be
+ * searched through relational searches that we do earlier in this method.
+ *
+ * For example, we do not store "location" as a field on the assets table, we store
+ * that relationship through location_id on the assets table, therefore querying
+ * assets.location would fail, as that field doesn't exist -- plus we're already searching
+ * against those relationships earlier in this method.
+ *
+ * - snipe
+ */
+
+ if (($fieldname!='category') && ($fieldname!='model_number') && ($fieldname!='rtd_location') && ($fieldname!='location') && ($fieldname!='supplier')
+ && ($fieldname!='status_label') && ($fieldname!='assigned_to') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')
+ ) {
+ $query->where('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
+ }
- $fieldname = str_replace('custom_fields.', '', $key);
- if ($fieldname == 'asset_tag') {
- $query->where('assets.asset_tag', 'LIKE', '%'.$search_val.'%');
}
- if ($fieldname == 'name') {
- $query->where('assets.name', 'LIKE', '%'.$search_val.'%');
- }
-
-
- if ($fieldname =='serial') {
- $query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
- }
-
- if ($fieldname == 'purchase_date') {
- $query->where('assets.purchase_date', 'LIKE', '%'.$search_val.'%');
- }
-
- if ($fieldname == 'purchase_cost') {
- $query->where('assets.purchase_cost', 'LIKE', '%'.$search_val.'%');
- }
-
- if ($fieldname == 'notes') {
- $query->where('assets.notes', 'LIKE', '%'.$search_val.'%');
- }
-
- if ($fieldname == 'order_number') {
- $query->where('assets.order_number', 'LIKE', '%'.$search_val.'%');
- }
-
- if ($fieldname == 'status_label') {
- $query->whereHas('assetstatus', function ($query) use ($search_val) {
- $query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
- });
- }
-
- if ($fieldname == 'location') {
- $query->whereHas('location', function ($query) use ($search_val) {
- $query->where('locations.name', 'LIKE', '%'.$search_val.'%');
- });
- }
-
- if ($fieldname == 'rtd_location') {
- $query->whereHas('defaultLoc', function ($query) use ($search_val) {
- $query->where('locations.name', 'LIKE', '%'.$search_val.'%');
- });
- }
-
- if ($fieldname =='assigned_to') {
- $query->whereHasMorph('assignedTo', [User::class], function ($query) use ($search_val) {
- $query->where(function ($query) use ($search_val) {
- $query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
- ->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%');
- });
- });
- }
-
-
- if ($fieldname == 'manufacturer') {
- $query->whereHas('model', function ($query) use ($search_val) {
- $query->whereHas('manufacturer', function ($query) use ($search_val) {
- $query->where(function ($query) use ($search_val) {
- $query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
- });
- });
- });
- }
-
- if ($fieldname == 'category') {
- $query->whereHas('model', function ($query) use ($search_val) {
- $query->whereHas('category', function ($query) use ($search_val) {
- $query->where(function ($query) use ($search_val) {
- $query->where('categories.name', 'LIKE', '%'.$search_val.'%')
- ->orWhere('models.name', 'LIKE', '%'.$search_val.'%')
- ->orWhere('models.model_number', 'LIKE', '%'.$search_val.'%');
- });
- });
- });
- }
-
- if ($fieldname == 'model') {
- $query->where(function ($query) use ($search_val) {
- $query->whereHas('model', function ($query) use ($search_val) {
- $query->where('models.name', 'LIKE', '%'.$search_val.'%');
- });
- });
- }
-
- if ($fieldname == 'model_number') {
- $query->where(function ($query) use ($search_val) {
- $query->whereHas('model', function ($query) use ($search_val) {
- $query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
- });
- });
- }
-
-
- if ($fieldname == 'company') {
- $query->where(function ($query) use ($search_val) {
- $query->whereHas('company', function ($query) use ($search_val) {
- $query->where('companies.name', 'LIKE', '%'.$search_val.'%');
- });
- });
- }
-
- if ($fieldname == 'supplier') {
- $query->where(function ($query) use ($search_val) {
- $query->whereHas('supplier', function ($query) use ($search_val) {
- $query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
- });
- });
- }
-
-
- /**
- * THIS CLUNKY BIT IS VERY IMPORTANT
- *
- * Although inelegant, this section matters a lot when querying against fields that do not
- * exist on the asset table. There's probably a better way to do this moving forward, for
- * example using the Schema:: methods to determine whether or not a column actually exists,
- * or even just using the $searchableRelations variable earlier in this file.
- *
- * In short, this set of statements tells the query builder to ONLY query against an
- * actual field that's being passed if it doesn't meet known relational fields. This
- * allows us to query custom fields directly in the assets table
- * (regardless of their name) and *skip* any fields that we already know can only be
- * searched through relational searches that we do earlier in this method.
- *
- * For example, we do not store "location" as a field on the assets table, we store
- * that relationship through location_id on the assets table, therefore querying
- * assets.location would fail, as that field doesn't exist -- plus we're already searching
- * against those relationships earlier in this method.
- *
- * - snipe
- *
- */
-
- if (($fieldname!='category') && ($fieldname!='model_number') && ($fieldname!='rtd_location') && ($fieldname!='location') && ($fieldname!='supplier')
- && ($fieldname!='status_label') && ($fieldname!='assigned_to') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')) {
- $query->where('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
- }
-
}
-
-
- });
+ );
}
/**
- * Query builder scope to order on model
- *
- * @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 model
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $order Order
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeOrderModels($query, $order)
{
return $query->join('models as asset_models', 'assets.model_id', '=', 'asset_models.id')->orderBy('asset_models.name', $order);
}
/**
- * Query builder scope to order on model number
- *
- * @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 model number
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $order Order
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeOrderModelNumber($query, $order)
{
return $query->leftJoin('models as model_number_sort', 'assets.model_id', '=', 'model_number_sort.id')->orderBy('model_number_sort.model_number', $order);
@@ -1820,8 +1961,8 @@ class Asset extends Depreciable
/**
* Query builder scope to order on created_by 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
*/
@@ -1832,39 +1973,39 @@ class Asset extends Depreciable
/**
- * Query builder scope to order on assigned user
- *
- * @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 assigned user
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $order Order
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeOrderAssigned($query, $order)
{
return $query->leftJoin('users as users_sort', 'assets.assigned_to', '=', 'users_sort.id')->select('assets.*')->orderBy('users_sort.first_name', $order)->orderBy('users_sort.last_name', $order);
}
/**
- * Query builder scope to order on status
- *
- * @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 status
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $order Order
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
public function scopeOrderStatus($query, $order)
{
return $query->join('status_labels as status_sort', 'assets.status_id', '=', 'status_sort.id')->orderBy('status_sort.name', $order);
}
/**
- * 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 as company_sort', 'assets.company_id', '=', 'company_sort.id')->orderBy('company_sort.name', $order);
@@ -1874,8 +2015,8 @@ class Asset extends Depreciable
/**
* Query builder scope to return results of a category
*
- * @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
*/
@@ -1883,34 +2024,34 @@ class Asset extends Depreciable
{
return $query->join('models as category_models', 'assets.model_id', '=', 'category_models.id')
->join('categories', 'category_models.category_id', '=', 'categories.id')
- ->whereIn('category_models.category_id', (!is_array($category_id) ? explode(',',$category_id): $category_id));
+ ->whereIn('category_models.category_id', (!is_array($category_id) ? explode(',', $category_id): $category_id));
//->whereIn('category_models.category_id', $category_id);
}
/**
* Query builder scope to return results of a 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
*/
public function scopeByManufacturer($query, $manufacturer_id)
{
return $query->join('models', 'assets.model_id', '=', 'models.id')
- ->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->whereIn('models.manufacturer_id', (!is_array($manufacturer_id) ? explode(',',$manufacturer_id): $manufacturer_id));
+ ->join('manufacturers', 'models.manufacturer_id', '=', 'manufacturers.id')->whereIn('models.manufacturer_id', (!is_array($manufacturer_id) ? explode(',', $manufacturer_id): $manufacturer_id));
}
/**
- * 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->join('models as order_model_category', 'assets.model_id', '=', 'order_model_category.id')
@@ -1922,8 +2063,8 @@ class Asset extends Depreciable
/**
* 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
*/
@@ -1934,14 +2075,14 @@ class Asset extends Depreciable
->orderBy('manufacturer_order.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 as asset_locations', 'asset_locations.id', '=', 'assets.location_id')->orderBy('asset_locations.name', $order);
@@ -1949,8 +2090,9 @@ class Asset extends Depreciable
/**
* Query builder scope to order on default
- * @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
*/
@@ -1963,8 +2105,8 @@ class Asset extends Depreciable
/**
* Query builder scope to order on supplier 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
*/
@@ -1973,29 +2115,47 @@ class Asset extends Depreciable
return $query->leftJoin('suppliers as suppliers_assets', 'assets.supplier_id', '=', 'suppliers_assets.id')->orderBy('suppliers_assets.name', $order);
}
+ /**
+ * Query builder scope to order on supplier name
+ *
+ * @param \Illuminate\Database\Query\Builder $query Query builder instance
+ * @param text $order Order
+ *
+ * @return \Illuminate\Database\Query\Builder Modified query builder
+ */
+ public function scopeOrderByJobTitle($query, $order)
+ {
+ return $query->leftJoin('users as users_sort', 'assets.assigned_to', '=', 'users_sort.id')->select('assets.*')->orderBy('users_sort.jobtitle', $order);
+ }
+
/**
* Query builder scope to search on location ID
*
- * @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
*/
public function scopeByLocationId($query, $search)
{
- return $query->where(function ($query) use ($search) {
- $query->whereHas('location', function ($query) use ($search) {
- $query->where('locations.id', '=', $search);
- });
- });
+ return $query->where(
+ function ($query) use ($search) {
+ $query->whereHas(
+ 'location', function ($query) use ($search) {
+ $query->where('locations.id', '=', $search);
+ }
+ );
+ }
+ );
}
/**
* Query builder scope to search on depreciation 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
*/
diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php
index 8d60474b96..c73e4492ac 100755
--- a/app/Models/AssetModel.php
+++ b/app/Models/AssetModel.php
@@ -2,6 +2,7 @@
namespace App\Models;
+use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -16,7 +17,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
{
@@ -24,6 +25,7 @@ class AssetModel extends SnipeModel
use SoftDeletes;
use Loggable, Requestable, Presentable;
use TwoColumnUniqueUndeletedTrait;
+ use HasUploads;
/**
* Whether the model should inject its identifier to the unique
@@ -96,14 +98,22 @@ class AssetModel extends SnipeModel
'manufacturer' => ['name'],
];
+ protected static function booted(): void
+ {
+ static::forceDeleted(function (AssetModel $assetModel) {
+ $assetModel->requests()->forceDelete();
+ });
-
+ static::softDeleted(function (AssetModel $assetModel) {
+ $assetModel->requests()->delete();
+ });
+ }
/**
* Establishes the model -> assets relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -115,7 +125,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> category relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -127,7 +137,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> depreciation relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function depreciation()
@@ -139,7 +149,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> manufacturer relationship
*
* @author [A. Gianotto] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -151,7 +161,7 @@ class AssetModel extends SnipeModel
* Establishes the model -> fieldset relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function fieldset()
@@ -161,14 +171,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()
@@ -182,7 +192,7 @@ class AssetModel extends SnipeModel
* @todo this should probably be moved
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function getImageUrl()
@@ -199,7 +209,7 @@ class AssetModel extends SnipeModel
* Checks if the model is deletable
*
* @author A. Gianotto
- * @since [v6.3.4]
+ * @since [v6.3.4]
* @return bool
*/
public function isDeletable()
@@ -209,27 +219,12 @@ class AssetModel extends SnipeModel
&& ($this->deleted_at == '');
}
- /**
- * Get uploads for this model
- *
- * @author [A. Gianotto] []
- * @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] []
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -248,10 +243,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
* @version v1.0
*/
@@ -264,9 +259,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
* @version v3.5
*/
@@ -278,8 +273,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
*/
@@ -287,23 +282,31 @@ 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
*/
@@ -315,8 +318,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
*/
@@ -332,7 +335,6 @@ class AssetModel extends SnipeModel
/**
* Query builder scope to order on created_by name
- *
*/
public function scopeOrderByCreatedByName($query, $order)
{
diff --git a/app/Models/Category.php b/app/Models/Category.php
index 0321dfdb26..7d9d7a14ae 100755
--- a/app/Models/Category.php
+++ b/app/Models/Category.php
@@ -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,6 +32,7 @@ class Category extends SnipeModel
protected $hidden = ['created_by', 'deleted_at'];
protected $casts = [
+ 'alert_on_response' => 'boolean',
'created_by' => 'integer',
];
@@ -69,6 +70,7 @@ class Category extends SnipeModel
'eula_text',
'name',
'require_acceptance',
+ 'alert_on_response',
'use_default_eula',
'created_by',
'notes',
@@ -94,7 +96,7 @@ class Category extends SnipeModel
* Checks if category can be deleted
*
* @author [Dan Meltzer] []
- * @since [v5.0]
+ * @since [v5.0]
* @return bool
*/
public function isDeletable()
@@ -117,7 +119,7 @@ class Category extends SnipeModel
* Establishes the category -> accessories relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessories()
@@ -129,7 +131,7 @@ class Category extends SnipeModel
* Establishes the category -> licenses relationship
*
* @author [A. Gianotto] []
- * @since [v4.3]
+ * @since [v4.3]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -141,7 +143,7 @@ class Category extends SnipeModel
* Establishes the category -> consumables relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumables()
@@ -153,7 +155,7 @@ class Category extends SnipeModel
* Establishes the category -> consumables relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function components()
@@ -168,7 +170,7 @@ class Category extends SnipeModel
* It should only be used in a single category context.
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return int
*/
public function itemCount()
@@ -179,18 +181,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;
}
}
@@ -199,7 +201,7 @@ class Category extends SnipeModel
* Establishes the category -> assets relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -216,8 +218,8 @@ class Category extends SnipeModel
* by their category.
*
* @author [A. Gianotto] []
- * @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()
@@ -229,7 +231,7 @@ class Category extends SnipeModel
* Establishes the category -> models relationship
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function models()
@@ -247,7 +249,7 @@ class Category extends SnipeModel
* checks for a settings level EULA
*
* @author [A. Gianotto] []
- * @since [v2.0]
+ * @since [v2.0]
* @return string | null
*/
public function getEula()
@@ -274,7 +276,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)
@@ -291,9 +293,9 @@ class Category extends SnipeModel
/**
* Query builder scope for whether or not the category requires acceptance
*
- * @author Vincent Sposato
+ * @author Vincent Sposato
*
- * @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)
diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php
index e44a330ebc..0599286f85 100644
--- a/app/Models/CheckoutAcceptance.php
+++ b/app/Models/CheckoutAcceptance.php
@@ -15,6 +15,7 @@ class CheckoutAcceptance extends Model
protected $casts = [
'accepted_at' => 'datetime',
'declined_at' => 'datetime',
+ 'alert_on_response_id' => 'integer',
];
/**
@@ -65,7 +66,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)
@@ -78,7 +79,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)
{
@@ -98,7 +99,7 @@ class CheckoutAcceptance extends Model
/**
* Decline the checkout acceptance
*
- * @param string $signature_filename
+ * @param string $signature_filename
*/
public function decline($signature_filename, $note = null)
{
@@ -115,8 +116,9 @@ 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)
@@ -126,6 +128,7 @@ class CheckoutAcceptance extends Model
/**
* Filter to only get pending acceptances
+ *
* @param Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
diff --git a/app/Models/CheckoutRequest.php b/app/Models/CheckoutRequest.php
index d6a85f2972..42512d8fda 100644
--- a/app/Models/CheckoutRequest.php
+++ b/app/Models/CheckoutRequest.php
@@ -2,11 +2,13 @@
namespace App\Models;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class CheckoutRequest extends Model
{
+ use HasFactory;
use SoftDeletes;
protected $fillable = ['user_id'];
protected $table = 'checkout_requests';
diff --git a/app/Models/Company.php b/app/Models/Company.php
index 29fa99a4b9..a2f5ddc38b 100644
--- a/app/Models/Company.php
+++ b/app/Models/Company.php
@@ -13,11 +13,13 @@ 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';
@@ -26,19 +28,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;
@@ -100,7 +102,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)
@@ -127,7 +129,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)
@@ -146,10 +148,10 @@ final class Company extends SnipeModel
if (!is_string($companyable)) {
$company_table = $companyable->getModel()->getTable();
try {
- // This is primary for the gate:allows-check in location->isDeletable()
+ // This is primarily 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 (!$companyable instanceof Company && !Schema::hasColumn($company_table, 'company_id')) {
+ if (!Schema::hasColumn($company_table, 'company_id')) {
return true;
}
@@ -163,9 +165,16 @@ final class Company extends SnipeModel
// Log::warning('Companyable is '.$companyable);
$current_user_company_id = auth()->user()->company_id;
$companyable_company_id = $companyable->company_id;
- return $current_user_company_id == null || $current_user_company_id == $companyable_company_id || auth()->user()->isSuperUser();
+
+ // 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 false;
+
}
public static function isCurrentUserAuthorized()
@@ -183,7 +192,7 @@ final class Company extends SnipeModel
* Checks if company can be deleted
*
* @author [Dan Meltzer] []
- * @since [v5.0]
+ * @since [v5.0]
* @return bool
*/
public function isDeletable()
@@ -200,7 +209,7 @@ final class Company extends SnipeModel
}
/**
- * @param $unescaped_input
+ * @param $unescaped_input
* @return int|mixed|string|null
*/
public static function getIdForUser($unescaped_input)
@@ -256,14 +265,14 @@ final class Company extends SnipeModel
* @todo - refactor that trait to handle the user's model as well.
*
* @author [A. Gianotto]
- * @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 {
@@ -280,11 +289,16 @@ 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;
- } else {
- $company_id = null;
+ }
+
+
+ // 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);
}
@@ -297,6 +311,8 @@ final class Company extends SnipeModel
return $query->where($table.$column, '=', $company_id);
}
+
+
}
public function adminuser()
@@ -311,8 +327,8 @@ final class Company extends SnipeModel
* This gets invoked by CompanyableChildScope, but I'm not sure what it does.
*
* @author [A. Gianotto]
- * @param array $companyable_names
- * @param $query
+ * @param array $companyable_names
+ * @param $query
* @return mixed
*/
public static function scopeCompanyableChildren(array $companyable_names, $query)
@@ -324,17 +340,18 @@ 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;
}
diff --git a/app/Models/CompanyableChildScope.php b/app/Models/CompanyableChildScope.php
index 4077ebd596..35f2049e8b 100644
--- a/app/Models/CompanyableChildScope.php
+++ b/app/Models/CompanyableChildScope.php
@@ -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)
diff --git a/app/Models/CompanyableScope.php b/app/Models/CompanyableScope.php
index 4bbe7d6396..bbc0b5e0e6 100644
--- a/app/Models/CompanyableScope.php
+++ b/app/Models/CompanyableScope.php
@@ -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)
diff --git a/app/Models/CompanyableTrait.php b/app/Models/CompanyableTrait.php
index c4b9b89fbf..183e755086 100644
--- a/app/Models/CompanyableTrait.php
+++ b/app/Models/CompanyableTrait.php
@@ -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()
diff --git a/app/Models/Component.php b/app/Models/Component.php
index 0208fb9f68..a83993c225 100644
--- a/app/Models/Component.php
+++ b/app/Models/Component.php
@@ -2,17 +2,20 @@
namespace App\Models;
+use App\Helpers\Helper;
+use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
+use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
/**
* Model for Components.
*
- * @version v1.0
+ * @version v1.0
*/
class Component extends SnipeModel
{
@@ -20,6 +23,7 @@ class Component extends SnipeModel
protected $presenter = \App\Presenters\ComponentPresenter::class;
use CompanyableTrait;
+ use HasUploads;
use Loggable, Presentable;
use SoftDeletes;
protected $casts = [
@@ -113,28 +117,13 @@ class Component extends SnipeModel
&& ($this->deleted_at == '');
}
- /**
- * Establishes the components -> action logs -> uploads relationship
- *
- * @author A. Gianotto
- * @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 component -> location relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -146,7 +135,7 @@ class Component extends SnipeModel
* Establishes the component -> assets relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -160,7 +149,7 @@ class Component extends SnipeModel
* @todo this is probably not needed - refactor
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -172,7 +161,7 @@ class Component extends SnipeModel
* Establishes the component -> company relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -184,7 +173,7 @@ class Component extends SnipeModel
* Establishes the component -> category relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -196,7 +185,7 @@ class Component extends SnipeModel
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] []
- * @since [v6.1.1]
+ * @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
@@ -209,19 +198,49 @@ class Component extends SnipeModel
* Establishes the item -> manufacturer relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
{
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
}
+ /**
+ * Determine whether this asset requires acceptance by the assigned user
+ *
+ * @author [A. Gianotto] []
+ * @since [v4.0]
+ * @return bool
+ */
+ public function requireAcceptance()
+ {
+ return $this->category->require_acceptance;
+ }
+
+ /**
+ * Checks for a category-specific EULA, and if that doesn't exist,
+ * checks for a settings level EULA
+ *
+ * @author [A. Gianotto] []
+ * @since [v4.0]
+ * @return string | false
+ */
+ public function getEula()
+ {
+ if ($this->category->eula_text) {
+ return Helper::parseEscapedMarkedown($this->category->eula_text);
+ } elseif ((Setting::getSettings()->default_eula_text) && ($this->category->use_default_eula == '1')) {
+ return Helper::parseEscapedMarkedown(Setting::getSettings()->default_eula_text);
+ } else {
+ return null;
+ }
+ }
/**
* Establishes the component -> action logs relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
@@ -233,7 +252,7 @@ class Component extends SnipeModel
* Check how many items within a component are checked out
*
* @author [A. Gianotto] []
- * @since [v5.0]
+ * @since [v5.0]
* @return int
*/
public function numCheckedOut()
@@ -252,20 +271,34 @@ class Component extends SnipeModel
*
* This allows us to get the assets with assigned components without the company restriction
*/
- public function uncontrainedAssets() {
+ public function uncontrainedAssets()
+ {
return $this->belongsToMany(\App\Models\Asset::class, 'components_assets')
- ->withPivot('id', 'assigned_qty', 'created_at', 'created_by', 'note')
- ->withoutGlobalScope(new CompanyableScope);
+ ->withPivot('id', 'assigned_qty', 'created_at', 'created_by', 'note')
+ ->withoutGlobalScope(new CompanyableScope);
}
+ /**
+ * Determine whether to send a checkin/checkout email based on
+ * asset model category
+ *
+ * @author [A. Gianotto] []
+ * @since [v4.0]
+ * @return bool
+ */
+ public function checkin_email()
+ {
+ return $this->category?->checkin_email;
+ }
+
/**
* Check how many items within a component are remaining
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return int
*/
public function numRemaining()
@@ -288,8 +321,8 @@ class Component extends SnipeModel
* This simply checks that there is a value for quantity, and if there isn't, set it to 0.
*
* @author A. Gianotto
- * @since v6.3.4
- * @param $value
+ * @since v6.3.4
+ * @param $value
* @return void
*/
public function setQtyAttribute($value)
@@ -307,8 +340,8 @@ class Component extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
@@ -320,8 +353,8 @@ class Component extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
@@ -333,8 +366,8 @@ class Component extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
@@ -346,8 +379,8 @@ class Component extends SnipeModel
/**
* Query builder scope to order on supplier
*
- * @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
*/
@@ -359,8 +392,8 @@ class Component extends SnipeModel
/**
* 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
*/
diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php
index c83aa6106e..f4b6a3ca6b 100644
--- a/app/Models/Consumable.php
+++ b/app/Models/Consumable.php
@@ -4,6 +4,7 @@ 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;
@@ -29,6 +30,7 @@ class Consumable extends SnipeModel
use Loggable, Presentable;
use SoftDeletes;
use Acceptable;
+ use HasUploads;
protected $table = 'consumables';
protected $casts = [
@@ -111,21 +113,6 @@ class Consumable extends SnipeModel
];
- /**
- * Establishes the components -> action logs -> uploads relationship
- *
- * @author A. Gianotto
- * @since [v6.1.13]
- * @return \Illuminate\Database\Eloquent\Relations\Relation
- */
- public function uploads()
- {
- return $this->hasMany(Actionlog::class, 'item_id')
- ->where('item_type', '=', self::class)
- ->where('action_type', '=', 'uploaded')
- ->whereNotNull('filename')
- ->orderBy('created_at', 'desc');
- }
/**
@@ -138,7 +125,7 @@ class Consumable extends SnipeModel
* @todo Update this comment once it's been implemented
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function setRequestableAttribute($value)
@@ -153,7 +140,7 @@ class Consumable extends SnipeModel
* Establishes the consumable -> admin user relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -165,7 +152,7 @@ class Consumable extends SnipeModel
* Establishes the component -> assignments relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function consumableAssignments()
@@ -177,7 +164,7 @@ class Consumable extends SnipeModel
* Establishes the component -> company relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -189,7 +176,7 @@ class Consumable extends SnipeModel
* Establishes the component -> manufacturer relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -201,7 +188,7 @@ class Consumable extends SnipeModel
* Establishes the component -> location relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -213,7 +200,7 @@ class Consumable extends SnipeModel
* Establishes the component -> category relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -226,7 +213,7 @@ class Consumable extends SnipeModel
* Establishes the component -> action logs relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assetlog()
@@ -238,7 +225,7 @@ class Consumable extends SnipeModel
* Gets the full image url for the consumable
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string | false
*/
public function getImageUrl()
@@ -254,7 +241,7 @@ class Consumable extends SnipeModel
* Establishes the component -> users relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
*/
public function users() : Relation
{
@@ -265,7 +252,7 @@ class Consumable extends SnipeModel
* Establishes the item -> supplier relationship
*
* @author [A. Gianotto] []
- * @since [v6.1.1]
+ * @since [v6.1.1]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function supplier()
@@ -279,19 +266,19 @@ class Consumable extends SnipeModel
* asset model category
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return bool
*/
public function checkin_email()
{
- return $this->category->checkin_email;
+ return $this->category?->checkin_email;
}
/**
* Determine whether this asset requires acceptance by the assigned user
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return bool
*/
public function requireAcceptance()
@@ -304,7 +291,7 @@ class Consumable extends SnipeModel
* checks for a settings level EULA
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return string | false
*/
public function getEula()
@@ -322,7 +309,7 @@ class Consumable extends SnipeModel
* Check how many items within a consumable are checked out
*
* @author [A. Gianotto] []
- * @since [v5.0]
+ * @since [v5.0]
* @return int
*/
public function numCheckedOut()
@@ -334,7 +321,7 @@ class Consumable extends SnipeModel
* Checks the number of available consumables
*
* @author [A. Gianotto] []
- * @since [v4.0]
+ * @since [v4.0]
* @return int
*/
public function numRemaining()
@@ -360,8 +347,8 @@ class Consumable extends SnipeModel
* This simply checks that there is a value for quantity, and if there isn't, set it to 0.
*
* @author A. Gianotto
- * @since v6.3.4
- * @param $value
+ * @since v6.3.4
+ * @param $value
* @return void
*/
public function setQtyAttribute($value)
@@ -378,8 +365,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
@@ -391,8 +378,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on location
*
- * @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
*/
@@ -404,8 +391,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on manufacturer
*
- * @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
*/
@@ -417,8 +404,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
@@ -430,8 +417,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on remaining
*
- * @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
*/
@@ -444,8 +431,8 @@ class Consumable extends SnipeModel
/**
* Query builder scope to order on supplier
*
- * @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
*/
diff --git a/app/Models/CustomField.php b/app/Models/CustomField.php
index b3742882a2..0e8845cfb3 100644
--- a/app/Models/CustomField.php
+++ b/app/Models/CustomField.php
@@ -16,6 +16,7 @@ class CustomField extends Model
UniqueUndeletedTrait;
/**
+ *
* Custom field predfined formats
*
* @var array
@@ -92,7 +93,7 @@ class CustomField extends Model
* table instead of the assets table.
*
* @author [Brady Wetherington] []
- * @since [v3.0]
+ * @since [v3.0]
*/
public static $table_name = 'assets';
@@ -103,7 +104,7 @@ class CustomField extends Model
* do with previously existing values. - @snipe
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return string
*/
public static function name_to_db_name($name)
@@ -120,66 +121,78 @@ class CustomField extends Model
* to do it in the controllers.
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return bool
*/
public static function boot()
{
parent::boot();
- self::created(function ($custom_field) {
+ self::created(
+ function ($custom_field) {
- // Column already exists on the assets table - nothing to do here.
- // This *shouldn't* happen in the wild.
- if (Schema::hasColumn(self::$table_name, $custom_field->db_column)) {
- return false;
+ // Column already exists on the assets table - nothing to do here.
+ // This *shouldn't* happen in the wild.
+ if (Schema::hasColumn(self::$table_name, $custom_field->db_column)) {
+ return false;
+ }
+
+ // Update the column name in the assets table
+ Schema::table(
+ self::$table_name, function ($table) use ($custom_field) {
+ $table->text($custom_field->convertUnicodeDbSlug())->nullable();
+ }
+ );
+
+ // Update the db_column property in the custom fields table
+ $custom_field->db_column = $custom_field->convertUnicodeDbSlug();
+ $custom_field->save();
}
+ );
- // Update the column name in the assets table
- Schema::table(self::$table_name, function ($table) use ($custom_field) {
- $table->text($custom_field->convertUnicodeDbSlug())->nullable();
- });
+ self::updating(
+ function ($custom_field) {
- // Update the db_column property in the custom fields table
- $custom_field->db_column = $custom_field->convertUnicodeDbSlug();
- $custom_field->save();
- });
+ // Column already exists on the assets table - nothing to do here.
+ if ($custom_field->isDirty('name')) {
+ if (Schema::hasColumn(self::$table_name, $custom_field->convertUnicodeDbSlug())) {
+ return true;
+ }
- self::updating(function ($custom_field) {
+ // Rename the field if the name has changed
+ Schema::table(
+ self::$table_name, function ($table) use ($custom_field) {
+ $table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug());
+ }
+ );
+
+ // Save the updated column name to the custom fields table
+ $custom_field->db_column = $custom_field->convertUnicodeDbSlug();
+ $custom_field->save();
- // Column already exists on the assets table - nothing to do here.
- if ($custom_field->isDirty('name')) {
- if (Schema::hasColumn(self::$table_name, $custom_field->convertUnicodeDbSlug())) {
return true;
}
- // Rename the field if the name has changed
- Schema::table(self::$table_name, function ($table) use ($custom_field) {
- $table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug());
- });
-
- // Save the updated column name to the custom fields table
- $custom_field->db_column = $custom_field->convertUnicodeDbSlug();
- $custom_field->save();
-
return true;
}
-
- return true;
- });
+ );
// Drop the assets column if we've deleted it from custom fields
- self::deleting(function ($custom_field) {
- return Schema::table(self::$table_name, function ($table) use ($custom_field) {
- $table->dropColumn($custom_field->db_column);
- });
- });
+ self::deleting(
+ function ($custom_field) {
+ return Schema::table(
+ self::$table_name, function ($table) use ($custom_field) {
+ $table->dropColumn($custom_field->db_column);
+ }
+ );
+ }
+ );
}
/**
* Establishes the customfield -> fieldset relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function fieldset()
@@ -214,26 +227,26 @@ class CustomField extends Model
public function displayFieldInCurrentForm($form_type = null)
{
switch ($form_type) {
- case 'audit':
- return $this->displayFieldInAuditForm();
- case 'checkin':
- return $this->displayFieldInCheckinForm();
- case 'checkout':
- return $this->displayFieldInCheckoutForm();
+ case 'audit':
+ return $this->displayFieldInAuditForm();
+ case 'checkin':
+ return $this->displayFieldInCheckinForm();
+ case 'checkout':
+ return $this->displayFieldInCheckoutForm();
}
}
public function assetModels()
{
- return $this->fieldset()->with('models')->get()->pluck('models')->flatten()->unique('id');
+ return $this->fieldset()->with('models')->get()->pluck('models')->flatten()->unique('id');
}
/**
* Establishes the customfield -> admin user relationship
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
@@ -245,7 +258,7 @@ class CustomField extends Model
* Establishes the customfield -> default values relationship
*
* @author Hannah Tinkler
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function defaultValues()
@@ -262,19 +275,23 @@ class CustomField extends Model
*/
public function defaultValue($modelId)
{
- return $this->defaultValues->filter(function ($item) use ($modelId) {
- return $item->pivot->asset_model_id == $modelId;
- })->map(function ($item) {
- return $item->pivot->default_value;
- })->first();
+ return $this->defaultValues->filter(
+ function ($item) use ($modelId) {
+ return $item->pivot->asset_model_id == $modelId;
+ }
+ )->map(
+ function ($item) {
+ return $item->pivot->default_value;
+ }
+ )->first();
}
/**
* Checks the format of the attribute
*
* @author [A. Gianotto] []
- * @param $value string
- * @since [v3.0]
+ * @param $value string
+ * @since [v3.0]
* @return bool
*/
public function check_format($value)
@@ -286,7 +303,7 @@ class CustomField extends Model
* Gets the DB column name.
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return string
*/
public function db_column_name()
@@ -302,7 +319,7 @@ class CustomField extends Model
* user-friendly text in the dropdowns, and in the custom fields display.
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return string
*/
public function getFormatAttribute($value)
@@ -320,7 +337,7 @@ class CustomField extends Model
* Format a value string as an array for select boxes and checkboxes.
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return array
*/
public function setFormatAttribute($value)
@@ -336,7 +353,7 @@ class CustomField extends Model
* Format a value string as an array for select boxes and checkboxes.
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return array
*/
public function formatFieldValuesAsArray()
@@ -366,7 +383,7 @@ class CustomField extends Model
* Check whether the field is encrypted
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return bool
*/
public function isFieldDecryptable($string)
@@ -383,7 +400,7 @@ class CustomField extends Model
* won't break the database.
*
* @author [A. Gianotto] []
- * @since [v3.4]
+ * @since [v3.4]
* @return string
*/
public function convertUnicodeDbSlug($original = null)
@@ -392,7 +409,7 @@ class CustomField extends Model
$id = $this->id ? $this->id : 'xx';
if (! function_exists('transliterator_transliterate')) {
- $long_slug = '_snipeit_'.str_slug(mb_convert_encoding(trim($name),"UTF-8"), '_');
+ $long_slug = '_snipeit_'.str_slug(mb_convert_encoding(trim($name), "UTF-8"), '_');
} else {
$long_slug = '_snipeit_'.Utf8Slugger::slugify($name, '_');
}
@@ -402,9 +419,10 @@ class CustomField extends Model
/**
* Get validation rules for custom fields to use with Validator
+ *
* @author [V. Cordes] []
- * @param int $id
- * @since [v4.1.10]
+ * @param int $id
+ * @since [v4.1.10]
* @return array
*/
public function validationRules($regex_format = null)
@@ -418,6 +436,7 @@ class CustomField extends Model
/**
* Check to see if there is a custom regex format type
+ *
* @see https://github.com/grokability/snipe-it/issues/5896
*
* @author Wes Hulette
diff --git a/app/Models/CustomFieldset.php b/app/Models/CustomFieldset.php
index e5490b49b3..f27b838647 100644
--- a/app/Models/CustomFieldset.php
+++ b/app/Models/CustomFieldset.php
@@ -3,12 +3,19 @@
namespace App\Models;
use App\Rules\AlphaEncrypted;
+use App\Rules\BooleanEncrypted;
+use App\Rules\DateEncrypted;
+use App\Rules\EmailEncrypted;
+use App\Rules\IPEncrypted;
+use App\Rules\IPv4Encrypted;
+use App\Rules\IPv6Encrypted;
+use App\Rules\MacEncrypted;
use App\Rules\NumericEncrypted;
+use App\Rules\RegexEncrypted;
+use App\Rules\UrlEncrypted;
use Gate;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Support\Facades\Log;
-use Illuminate\Validation\Rule;
use Watson\Validating\ValidatingTrait;
class CustomFieldset extends Model
@@ -20,6 +27,7 @@ class CustomFieldset extends Model
/**
* Validation rules
+ *
* @var array
*/
public $rules = [
@@ -39,7 +47,7 @@ class CustomFieldset extends Model
* Establishes the fieldset -> field relationship
*
* @author [Brady Wetherington] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function fields()
@@ -51,7 +59,7 @@ class CustomFieldset extends Model
* Establishes the fieldset -> models relationship
*
* @author [Brady Wetherington] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function models()
@@ -63,7 +71,7 @@ class CustomFieldset extends Model
* Establishes the fieldset -> admin user relationship
*
* @author [Brady Wetherington] []
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
@@ -76,14 +84,14 @@ class CustomFieldset extends Model
if ($this->fields) {
switch ($form_type) {
- case 'audit':
- return $this->fields->where('display_audit', '1')->count() > 0;
- case 'checkin':
- return $this->fields->where('display_checkin', '1')->count() > 0;
- case 'checkout':
- return $this->fields->where('display_checkout', '1')->count() > 0;
- default:
- return true;
+ case 'audit':
+ return $this->fields->where('display_audit', '1')->count() > 0;
+ case 'checkin':
+ return $this->fields->where('display_checkin', '1')->count() > 0;
+ case 'checkout':
+ return $this->fields->where('display_checkout', '1')->count() > 0;
+ default:
+ return true;
}
}
@@ -95,17 +103,18 @@ class CustomFieldset extends Model
* custom field format
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return array
*/
- public function validation_rules()
+ public function validation_rules(): array
{
$rules = [];
foreach ($this->fields as $field) {
$rule = [];
- if (($field->field_encrypted != '1') ||
- (($field->field_encrypted == '1') && (Gate::allows('admin')))) {
+ if (($field->field_encrypted != '1')
+ || (($field->field_encrypted == '1') && (Gate::allows('admin')))
+ ) {
$rule[] = ($field->pivot->required == '1') ? 'required' : 'nullable';
}
@@ -119,18 +128,65 @@ class CustomFieldset extends Model
$rules[$field->db_column_name()] = $rule;
-
- // these are to replace the standard 'numeric' and 'alpha' rules if the custom field is also encrypted.
- // the values need to be decrypted first, because encrypted strings are alphanumeric
- if ($field->format === 'NUMERIC' && $field->field_encrypted) {
+ // this is to switch the rules to rules specially made for encrypted custom fields that decrypt the value before validating
+ if ($field->field_encrypted) {
$numericKey = array_search('numeric', $rules[$field->db_column_name()]);
- $rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
+ $alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
+ $emailKey = array_search('email', $rules[$field->db_column_name()]);
+ $dateKey = array_search('date', $rules[$field->db_column_name()]);
+ $urlKey = array_search('url', $rules[$field->db_column_name()]);
+ $ipKey = array_search('ip', $rules[$field->db_column_name()]);
+ $ipv4Key = array_search('ipv4', $rules[$field->db_column_name()]);
+ $ipv6Key = array_search('ipv6', $rules[$field->db_column_name()]);
+ $macKey = array_search('regex:/^[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}:[a-fA-F0-9]{2}$/', $rules[$field->db_column_name()]);
+ $booleanKey = array_search('boolean', $rules[$field->db_column_name()]);
+ // find objects in array that start with "regex:"
+ // collect because i couldn't figure how to do this
+ // with array filter and get keys out of it
+ $regexCollect = collect($rules[$field->db_column_name()]);
+ $regexKeys = $regexCollect->filter(function ($value, $key) {
+ return starts_with($value, 'regex:');
+ })->keys()->values()->toArray();
+
+ switch ($field->format) {
+ case 'NUMERIC':
+ $rules[$field->db_column_name()][$numericKey] = new NumericEncrypted;
+ break;
+ case 'ALPHA':
+ $rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
+ break;
+ case 'EMAIL':
+ $rules[$field->db_column_name()][$emailKey] = new EmailEncrypted;
+ break;
+ case 'DATE':
+ $rules[$field->db_column_name()][$dateKey] = new DateEncrypted;
+ break;
+ case 'URL':
+ $rules[$field->db_column_name()][$urlKey] = new UrlEncrypted;
+ break;
+ case 'IP':
+ $rules[$field->db_column_name()][$ipKey] = new IPEncrypted;
+ break;
+ case 'IPV4':
+ $rules[$field->db_column_name()][$ipv4Key] = new IPv4Encrypted;
+ break;
+ case 'IPV6':
+ $rules[$field->db_column_name()][$ipv6Key] = new IPv6Encrypted;
+ break;
+ case 'MAC':
+ $rules[$field->db_column_name()][$macKey] = new MacEncrypted;
+ break;
+ case 'BOOLEAN':
+ $rules[$field->db_column_name()][$booleanKey] = new BooleanEncrypted;
+ break;
+ case starts_with($field->format, 'regex'):
+ foreach ($regexKeys as $regexKey) {
+ $rules[$field->db_column_name()][$regexKey] = new RegexEncrypted;
+ }
+ break;
+ }
}
- if ($field->format === 'ALPHA' && $field->field_encrypted) {
- $alphaKey = array_search('alpha', $rules[$field->db_column_name()]);
- $rules[$field->db_column_name()][$alphaKey] = new AlphaEncrypted;
- }
// add not_array to rules for all fields but checkboxes
if ($field->element != 'checkbox') {
diff --git a/app/Models/Department.php b/app/Models/Department.php
index 592fd840b1..79616101e3 100644
--- a/app/Models/Department.php
+++ b/app/Models/Department.php
@@ -72,7 +72,7 @@ class Department extends SnipeModel
* Establishes the department -> company relationship
*
* @author A. Gianotto
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -84,7 +84,7 @@ class Department extends SnipeModel
* Establishes the department -> users relationship
*
* @author A. Gianotto
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function users()
@@ -96,7 +96,7 @@ class Department extends SnipeModel
* Establishes the department -> manager relationship
*
* @author A. Gianotto
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manager()
@@ -108,7 +108,7 @@ class Department extends SnipeModel
* Establishes the department -> location relationship
*
* @author A. Gianotto
- * @since [v4.0]
+ * @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function location()
@@ -119,8 +119,8 @@ class Department extends SnipeModel
/**
* Query builder scope to order on location 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
*/
@@ -132,8 +132,8 @@ class Department extends SnipeModel
/**
* Query builder scope to order on manager 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
*/
@@ -145,8 +145,8 @@ class Department extends SnipeModel
/**
* Query builder scope to order on company
*
- * @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
*/
diff --git a/app/Models/Depreciable.php b/app/Models/Depreciable.php
index 0c513a3d33..b4cd40e1d1 100644
--- a/app/Models/Depreciable.php
+++ b/app/Models/Depreciable.php
@@ -48,15 +48,15 @@ class Depreciable extends SnipeModel
$depreciation = 0;
$setting = Setting::getSettings();
switch ($setting->depreciation_method) {
- case 'half_1':
+ case 'half_1':
$depreciation = $this->getHalfYearDepreciatedValue(true);
break;
- case 'half_2':
+ case 'half_2':
$depreciation = $this->getHalfYearDepreciatedValue(false);
break;
- default:
+ default:
$depreciation = $this->getLinearDepreciatedValue();
}
@@ -74,7 +74,7 @@ class Depreciable extends SnipeModel
return null;
}
- if ($months_passed >= $this->get_depreciation()->months){
+ if ($months_passed >= $this->get_depreciation()->months) {
//if there is a floor use it
if($this->get_depreciation()->depreciation_min) {
@@ -93,14 +93,15 @@ class Depreciable extends SnipeModel
return $current_value;
}
- public function getMonthlyDepreciation(){
+ public function getMonthlyDepreciation()
+ {
return ($this->purchase_cost-$this->calculateDepreciation())/$this->get_depreciation()->months;
}
/**
- * @param onlyHalfFirstYear Boolean always applied only second half of the first year
+ * @param onlyHalfFirstYear Boolean always applied only second half of the first year
* @return float|int
*/
public function getHalfYearDepreciatedValue($onlyHalfFirstYear = false)
@@ -131,7 +132,7 @@ class Depreciable extends SnipeModel
}
/**
- * @param \DateTime $date
+ * @param \DateTime $date
* @return int
*/
protected function get_fiscal_year($date)
@@ -146,7 +147,7 @@ class Depreciable extends SnipeModel
}
/**
- * @param \DateTime $date
+ * @param \DateTime $date
* @return bool
*/
protected function is_first_half_of_year($date)
diff --git a/app/Models/Depreciation.php b/app/Models/Depreciation.php
index 11ee82c16a..6e01c6d782 100755
--- a/app/Models/Depreciation.php
+++ b/app/Models/Depreciation.php
@@ -16,7 +16,7 @@ class Depreciation extends SnipeModel
// Declare the rules for the form validation
protected $rules = [
'name' => 'required|min:3|max:255|unique:depreciations,name',
- 'months' => 'required|max:3600|integer|gt:0',
+ 'months' => 'required|max:3600|integer',
];
/**
@@ -56,7 +56,7 @@ class Depreciation extends SnipeModel
* Establishes the depreciation -> models relationship
*
* @author A. Gianotto
- * @since [v5.0]
+ * @since [v5.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function models()
@@ -68,7 +68,7 @@ class Depreciation extends SnipeModel
* Establishes the depreciation -> licenses relationship
*
* @author A. Gianotto
- * @since [v5.0]
+ * @since [v5.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function licenses()
@@ -80,7 +80,7 @@ class Depreciation extends SnipeModel
* Establishes the depreciation -> assets relationship
*
* @author A. Gianotto
- * @since [v5.0]
+ * @since [v5.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assets()
@@ -92,7 +92,7 @@ class Depreciation extends SnipeModel
* Get the user that created the depreciation
*
* @author A. Gianotto
- * @since [v7.0.13]
+ * @since [v7.0.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
diff --git a/app/Models/Group.php b/app/Models/Group.php
index 6dae273998..9f4f2e2e56 100755
--- a/app/Models/Group.php
+++ b/app/Models/Group.php
@@ -51,7 +51,7 @@ class Group extends SnipeModel
* Establishes the groups -> users relationship
*
* @author A. Gianotto
- * @since [v1.0]
+ * @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function users()
@@ -63,7 +63,7 @@ class Group extends SnipeModel
* Get the user that created the group
*
* @author A. Gianotto
- * @since [v6.3.0]
+ * @since [v6.3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
@@ -75,7 +75,7 @@ class Group extends SnipeModel
* Decode JSON permissions into array
*
* @author A. Gianotto
- * @since [v1.0]
+ * @since [v1.0]
* @return array | \stdClass
*/
public function decodePermissions()
diff --git a/app/Models/Import.php b/app/Models/Import.php
index d824a3840c..4ea259ba05 100644
--- a/app/Models/Import.php
+++ b/app/Models/Import.php
@@ -19,7 +19,7 @@ class Import extends Model
* Establishes the license -> admin user relationship
*
* @author A. Gianotto
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
diff --git a/app/Models/Labels/DefaultLabel.php b/app/Models/Labels/DefaultLabel.php
index f869110138..fa60dafab3 100644
--- a/app/Models/Labels/DefaultLabel.php
+++ b/app/Models/Labels/DefaultLabel.php
@@ -38,7 +38,8 @@ class DefaultLabel extends RectangleSheet
private int $rows;
- public function __construct() {
+ public function __construct()
+ {
$settings = Setting::getSettings();
$this->textSize = Helper::convertUnit($settings->labels_fontsize, 'pt', 'in');
@@ -74,41 +75,116 @@ class DefaultLabel extends RectangleSheet
}
- public function getUnit() { return 'in'; }
+ public function getUnit()
+ {
+ return 'in';
+ }
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginBottom; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginRight; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginBottom;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginRight;
+ }
- public function getColumns() { return $this->columns; }
- public function getRows() { return $this->rows; }
- public function getLabelBorder() { return 0; }
+ public function getColumns()
+ {
+ return $this->columns;
+ }
+ public function getRows()
+ {
+ return $this->rows;
+ }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelMarginTop() { return 0; }
- public function getLabelMarginBottom() { return 0; }
- public function getLabelMarginLeft() { return 0; }
- public function getLabelMarginRight() { return 0; }
+ public function getLabelMarginTop()
+ {
+ return 0;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 0;
+ }
+ public function getLabelMarginRight()
+ {
+ return 0;
+ }
- public function getLabelColumnSpacing() { return $this->labelSpacingH; }
- public function getLabelRowSpacing() { return $this->labelSpacingV; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->labelSpacingH;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->labelSpacingV;
+ }
- public function getSupportAssetTag() { return false; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 4; }
- public function getSupportTitle() { return true; }
- public function getSupportLogo() { return true; }
+ public function getSupportAssetTag()
+ {
+ return false;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 4;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
+ public function getSupportLogo()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$asset = $record->get('asset');
$settings = Setting::getSettings();
diff --git a/app/Models/Labels/Field.php b/app/Models/Labels/Field.php
index c023f54175..cdd40cac49 100644
--- a/app/Models/Labels/Field.php
+++ b/app/Models/Labels/Field.php
@@ -5,21 +5,30 @@ namespace App\Models\Labels;
use App\Models\Asset;
use Illuminate\Support\Collection;
-class Field {
+class Field
+{
protected Collection $options;
- public function getOptions() { return $this->options; }
- public function setOptions($options) {
+ public function getOptions()
+ {
+ return $this->options;
+ }
+ public function setOptions($options)
+ {
$tempCollect = collect($options);
if (!$tempCollect->contains(fn($o) => !is_subclass_of($o, FieldOption::class))) {
$this->options = $options;
}
}
- public function toArray(Asset $asset) { return Field::makeArray($this, $asset); }
+ public function toArray(Asset $asset)
+ {
+ return Field::makeArray($this, $asset);
+ }
/* Statics */
- public static function makeArray(Field $field, Asset $asset) {
+ public static function makeArray(Field $field, Asset $asset)
+ {
return $field->getOptions()
// filter out any FieldOptions that are accidentally null
->filter()
@@ -27,11 +36,13 @@ class Field {
->filter(fn($result) => $result['value'] != null);
}
- public static function makeString(Field $option) {
+ public static function makeString(Field $option)
+ {
return implode('|', $option->getOptions());
}
- public static function fromString(string $theString) {
+ public static function fromString(string $theString)
+ {
$field = new Field();
$field->options = collect(explode('|', $theString))
->filter(fn($optionString) => !empty($optionString))
diff --git a/app/Models/Labels/FieldOption.php b/app/Models/Labels/FieldOption.php
index 94394eda23..916707b21d 100644
--- a/app/Models/Labels/FieldOption.php
+++ b/app/Models/Labels/FieldOption.php
@@ -5,14 +5,22 @@ namespace App\Models\Labels;
use App\Models\Asset;
use Illuminate\Support\Collection;
-class FieldOption {
+class FieldOption
+{
protected string $label;
- public function getLabel() { return $this->label; }
+ public function getLabel()
+ {
+ return $this->label;
+ }
protected string $dataSource;
- public function getDataSource() { return $this->dataSource; }
+ public function getDataSource()
+ {
+ return $this->dataSource;
+ }
- public function getValue(Asset $asset) {
+ public function getValue(Asset $asset)
+ {
$dataPath = collect(explode('.', $this->dataSource));
// assignedTo directly on the asset is a special case where
@@ -33,18 +41,29 @@ class FieldOption {
return $asset->purchase_date ? $asset->purchase_date->format('Y-m-d') : null;
}
- return $dataPath->reduce(function ($myValue, $path) {
- try { return $myValue ? $myValue->{$path} : ${$myValue}; }
- catch (\Exception $e) { return $myValue; }
- }, $asset);
+ return $dataPath->reduce(
+ function ($myValue, $path) {
+ try { return $myValue ? $myValue->{$path} : ${$myValue};
+ }
+ catch (\Exception $e) { return $myValue;
+ }
+ }, $asset
+ );
}
- public function toArray(Asset $asset=null) { return FieldOption::makeArray($this, $asset); }
- public function toString() { return FieldOption::makeString($this); }
+ public function toArray(Asset $asset=null)
+ {
+ return FieldOption::makeArray($this, $asset);
+ }
+ public function toString()
+ {
+ return FieldOption::makeString($this);
+ }
/* Statics */
- public static function makeArray(FieldOption $option, Asset $asset=null) {
+ public static function makeArray(FieldOption $option, Asset $asset=null)
+ {
return [
'label' => $option->getLabel(),
'dataSource' => $option->getDataSource(),
@@ -52,11 +71,13 @@ class FieldOption {
];
}
- public static function makeString(FieldOption $option) {
+ public static function makeString(FieldOption $option)
+ {
return $option->getLabel() . '=' . $option->getDataSource();
}
- public static function fromString(string $theString) {
+ public static function fromString(string $theString)
+ {
$parts = explode('=', $theString);
if (count($parts) == 2) {
$option = new FieldOption();
diff --git a/app/Models/Labels/Label.php b/app/Models/Labels/Label.php
index 2405da5401..cff859f359 100644
--- a/app/Models/Labels/Label.php
+++ b/app/Models/Labels/Label.php
@@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Log;
/**
* Model for Labels.
*
- * @version v1.0
+ * @version v1.0
*/
abstract class Label
{
@@ -32,7 +32,8 @@ abstract class Label
*
* @return int
*/
- public function getRotation() {
+ public function getRotation()
+ {
return 0;
}
@@ -123,29 +124,32 @@ abstract class Label
/**
* Make changes to the PDF properties here. OPTIONAL.
*
- * @param TCPDF $pdf The TCPDF instance
+ * @param TCPDF $pdf The TCPDF instance
*/
public abstract function preparePDF(TCPDF $pdf);
/**
* Write single data record as content here.
*
- * @param TCPDF $pdf The TCPDF instance
- * @param Collection $record A data record
+ * @param TCPDF $pdf The TCPDF instance
+ * @param Collection $record A data record
*/
public abstract function write(TCPDF $pdf, Collection $record);
/**
* Handle the data here. Override for multiple-per-page handling
*
- * @param TCPDF $pdf The TCPDF instance
- * @param Collection $data The data
+ * @param TCPDF $pdf The TCPDF instance
+ * @param Collection $data The data
*/
- public function writeAll(TCPDF $pdf, Collection $data) {
- $data->each(function ($record, $index) use ($pdf) {
- $pdf->AddPage();
- $this->write($pdf, $record);
- });
+ public function writeAll(TCPDF $pdf, Collection $data)
+ {
+ $data->each(
+ function ($record, $index) use ($pdf) {
+ $pdf->AddPage();
+ $this->write($pdf, $record);
+ }
+ );
}
/**
@@ -153,7 +157,8 @@ abstract class Label
*
* @return string
*/
- public final function getName() {
+ public final function getName()
+ {
$refClass = new \ReflectionClass(Label::class);
return str_replace($refClass->getNamespaceName() . '\\', '', get_class($this));
}
@@ -165,7 +170,8 @@ abstract class Label
*
* @return string
*/
- public final function getOrientation() {
+ public final function getOrientation()
+ {
return ($this->getWidth() >= $this->getHeight()) ? 'L' : 'P';
}
@@ -174,7 +180,8 @@ abstract class Label
*
* @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ]
*/
- public final function getPrintableArea() {
+ public final function getPrintableArea()
+ {
return (object)[
'x1' => $this->getMarginLeft(),
'y1' => $this->getMarginTop(),
@@ -188,21 +195,22 @@ abstract class Label
/**
* Write a text cell.
*
- * @param TCPDF $pdf The TCPDF instance
- * @param string $text The text to write. Supports 'some **bold** text'.
- * @param float $x X position of top-left
- * @param float $y Y position of top-left
- * @param string $font The font family
- * @param string $style The font style
- * @param int $size The font size in getUnit() units
- * @param string $align Align text in the box. 'L' left, 'R' right, 'C' center.
- * @param float $width Force text box width. NULL to auto-fit.
- * @param float $height Force text box height. NULL to auto-fit.
- * @param bool $squash Squash text if it's too big
- * @param int $border Thickness of border. Default = 0.
- * @param int $spacing Letter spacing. Default = 0.
+ * @param TCPDF $pdf The TCPDF instance
+ * @param string $text The text to write. Supports 'some **bold** text'.
+ * @param float $x X position of top-left
+ * @param float $y Y position of top-left
+ * @param string $font The font family
+ * @param string $style The font style
+ * @param int $size The font size in getUnit() units
+ * @param string $align Align text in the box. 'L' left, 'R' right, 'C' center.
+ * @param float $width Force text box width. NULL to auto-fit.
+ * @param float $height Force text box height. NULL to auto-fit.
+ * @param bool $squash Squash text if it's too big
+ * @param int $border Thickness of border. Default = 0.
+ * @param int $spacing Letter spacing. Default = 0.
*/
- public final function writeText(TCPDF $pdf, $text, $x, $y, $font=null, $style=null, $size=null, $align='L', $width=null, $height=null, $squash=false, $border=0, $spacing=0) {
+ public final function writeText(TCPDF $pdf, $text, $x, $y, $font=null, $style=null, $size=null, $align='L', $width=null, $height=null, $squash=false, $border=0, $spacing=0)
+ {
$prevFamily = $pdf->getFontFamily();
$prevStyle = $pdf->getFontStyle();
$prevSizePt = $pdf->getFontSizePt();
@@ -211,33 +219,42 @@ abstract class Label
$fontFamily = !empty($font) ? $font : $prevFamily;
$fontStyle = !empty($style) ? $style : $prevStyle;
- if ($size) $fontSizePt = Helper::convertUnit($size, $this->getUnit(), 'pt', true);
- else $fontSizePt = $prevSizePt;
+ if ($size) { $fontSizePt = Helper::convertUnit($size, $this->getUnit(), 'pt', true);
+ } else { $fontSizePt = $prevSizePt;
+ }
$pdf->SetFontSpacing($spacing);
$parts = collect(explode('**', $text))
- ->map(function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt) {
- $modStyle = ($index % 2 == 1) ? 'B' : $fontStyle;
- $pdf->setFont($fontFamily, $modStyle, $fontSizePt);
- return [
+ ->map(
+ function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt) {
+ $modStyle = ($index % 2 == 1) ? 'B' : $fontStyle;
+ $pdf->setFont($fontFamily, $modStyle, $fontSizePt);
+ return [
'text' => $part,
'text_width' => $pdf->GetStringWidth($part),
'font_family' => $fontFamily,
'font_style' => $modStyle,
'font_size' => $fontSizePt,
- ];
- });
+ ];
+ }
+ );
- $textWidth = $parts->reduce(function ($carry, $part) { return $carry += $part['text_width']; });
+ $textWidth = $parts->reduce(
+ function ($carry, $part) {
+ return $carry += $part['text_width'];
+ }
+ );
$cellWidth = !empty($width) ? $width : $textWidth;
if ($squash && ($textWidth > 0)) {
$scaleFactor = min(1.0, $cellWidth / $textWidth);
- $parts = $parts->map(function ($part, $index) use ($scaleFactor) {
- $part['text_width'] = $part['text_width'] * $scaleFactor;
- return $part;
- });
+ $parts = $parts->map(
+ function ($part, $index) use ($scaleFactor) {
+ $part['text_width'] = $part['text_width'] * $scaleFactor;
+ return $part;
+ }
+ );
}
$cellHeight = !empty($height) ? $height : Helper::convertUnit($fontSizePt, 'pt', $this->getUnit());
@@ -249,18 +266,23 @@ abstract class Label
}
switch($align) {
- case 'R': $startX = ($x + $cellWidth) - min($cellWidth, $textWidth); break;
- case 'C': $startX = ($x + ($cellWidth / 2)) - (min($cellWidth, $textWidth) / 2); break;
- case 'L':
- default: $startX = $x; break;
+ case 'R': $startX = ($x + $cellWidth) - min($cellWidth, $textWidth);
+ break;
+ case 'C': $startX = ($x + ($cellWidth / 2)) - (min($cellWidth, $textWidth) / 2);
+ break;
+ case 'L':
+ default: $startX = $x;
+ break;
}
- $parts->reduce(function ($currentX, $part) use ($pdf, $y, $cellHeight) {
- $pdf->SetXY($currentX, $y);
- $pdf->setFont($part['font_family'], $part['font_style'], $part['font_size']);
- $pdf->Cell($part['text_width'], $cellHeight, $part['text'], 0, 0, '', false, '', 1, true);
- return $currentX += $part['text_width'];
- }, $startX);
+ $parts->reduce(
+ function ($currentX, $part) use ($pdf, $y, $cellHeight) {
+ $pdf->SetXY($currentX, $y);
+ $pdf->setFont($part['font_family'], $part['font_style'], $part['font_size']);
+ $pdf->Cell($part['text_width'], $cellHeight, $part['text'], 0, 0, '', false, '', 1, true);
+ return $currentX += $part['text_width'];
+ }, $startX
+ );
$pdf->SetFont($prevFamily, $prevStyle, $prevSizePt);
$pdf->SetFontSpacing(0);
@@ -269,27 +291,30 @@ abstract class Label
/**
* Write an image.
*
- * @param TCPDF $pdf The TCPDF instance
- * @param string $image The image to write
- * @param float $x X position of top-left
- * @param float $y Y position of top-left
- * @param float $width The container width
- * @param float $height The container height
- * @param string $halign Align text in the box. 'L' left, 'R' right, 'C' center. Default 'L'.
- * @param string $valign Align text in the box. 'T' top, 'B' bottom, 'C' center. Default 'T'.
- * @param int $dpi Pixels per inch
- * @param bool $resize Resize to fit container
- * @param bool $stretch Stretch (vs Scale) to fit container
- * @param int $border Thickness of border. Default = 0.
+ * @param TCPDF $pdf The TCPDF instance
+ * @param string $image The image to write
+ * @param float $x X position of top-left
+ * @param float $y Y position of top-left
+ * @param float $width The container width
+ * @param float $height The container height
+ * @param string $halign Align text in the box. 'L' left, 'R' right, 'C' center. Default 'L'.
+ * @param string $valign Align text in the box. 'T' top, 'B' bottom, 'C' center. Default 'T'.
+ * @param int $dpi Pixels per inch
+ * @param bool $resize Resize to fit container
+ * @param bool $stretch Stretch (vs Scale) to fit container
+ * @param int $border Thickness of border. Default = 0.
*
* @return array Returns the final calculated size [w,h]
*/
- public final function writeImage(TCPDF $pdf, $image, $x, $y, $width=null, $height=null, $halign='L', $valign='L', $dpi=300, $resize=false, $stretch=false, $border=0) {
+ public final function writeImage(TCPDF $pdf, $image, $x, $y, $width=null, $height=null, $halign='L', $valign='L', $dpi=300, $resize=false, $stretch=false, $border=0)
+ {
- if (empty($image)) return [0,0];
+ if (empty($image)) { return [0,0];
+ }
$imageInfo = getimagesize($image);
- if (!$imageInfo) return [0,0]; // TODO: SVG or other
+ if (!$imageInfo) { return [0,0]; // TODO: SVG or other
+ }
$imageWidthPx = $imageInfo[0];
$imageHeightPx = $imageInfo[1];
@@ -342,18 +367,24 @@ abstract class Label
// Horizontal Position
switch ($halign) {
- case 'R': $originX = ($x + $containerWidth) - $outputWidth; break;
- case 'C': $originX = ($x + ($containerWidth / 2)) - ($outputWidth / 2); break;
- case 'L':
- default: $originX = $x; break;
+ case 'R': $originX = ($x + $containerWidth) - $outputWidth;
+ break;
+ case 'C': $originX = ($x + ($containerWidth / 2)) - ($outputWidth / 2);
+ break;
+ case 'L':
+ default: $originX = $x;
+ break;
}
// Vertical Position
switch ($valign) {
- case 'B': $originY = ($y + $containerHeight) - $outputHeight; break;
- case 'C': $originY = ($y + ($containerHeight / 2)) - ($outputHeight / 2); break;
- case 'T':
- default: $originY = $y; break;
+ case 'B': $originY = ($y + $containerHeight) - $outputHeight;
+ break;
+ case 'C': $originY = ($y + ($containerHeight / 2)) - ($outputHeight / 2);
+ break;
+ case 'T':
+ default: $originY = $y;
+ break;
}
// Actual Image
@@ -373,16 +404,18 @@ abstract class Label
/**
* Write a 1D barcode.
*
- * @param TCPDF $pdf The TCPDF instance
- * @param string $value The barcode content
- * @param string $type The barcode type
- * @param float $x X position of top-left
- * @param float $y Y position of top-left
- * @param float $width The container width
- * @param float $height The container height
+ * @param TCPDF $pdf The TCPDF instance
+ * @param string $value The barcode content
+ * @param string $type The barcode type
+ * @param float $x X position of top-left
+ * @param float $y Y position of top-left
+ * @param float $width The container width
+ * @param float $height The container height
*/
- public final function write1DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) {
- if (empty($value)) return;
+ public final function write1DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height)
+ {
+ if (empty($value)) { return;
+ }
try {
$pdf->write1DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
} catch (\Exception|TypeError $e) {
@@ -393,16 +426,18 @@ abstract class Label
/**
* Write a 2D barcode.
*
- * @param TCPDF $pdf The TCPDF instance
- * @param string $value The barcode content
- * @param string $type The barcode type
- * @param float $x X position of top-left
- * @param float $y Y position of top-left
- * @param float $width The container width
- * @param float $height The container height
+ * @param TCPDF $pdf The TCPDF instance
+ * @param string $value The barcode content
+ * @param string $type The barcode type
+ * @param float $x X position of top-left
+ * @param float $y Y position of top-left
+ * @param float $width The container width
+ * @param float $height The container height
*/
- public final function write2DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) {
- if (empty($value)) return;
+ public final function write2DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height)
+ {
+ if (empty($value)) { return;
+ }
$pdf->write2DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
}
@@ -411,127 +446,180 @@ abstract class Label
/**
* Checks the template is internally valid
*/
- public final function validate() : void {
+ public final function validate() : void
+ {
$this->validateUnits();
$this->validateSize();
$this->validateMargins();
$this->validateSupport();
}
- private function validateUnits() : void {
+ private function validateUnits() : void
+ {
$validUnits = [ 'pt', 'mm', 'cm', 'in' ];
$unit = $this->getUnit();
if (!in_array(strtolower($unit), $validUnits)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_value', [
- 'name' => 'getUnit()',
- 'expected' => '[ \''.implode('\', \'', $validUnits).'\' ]',
- 'actual' => '\''.$unit.'\''
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_value', [
+ 'name' => 'getUnit()',
+ 'expected' => '[ \''.implode('\', \'', $validUnits).'\' ]',
+ 'actual' => '\''.$unit.'\''
+ ]
+ )
+ );
}
}
- private function validateSize() : void {
+ private function validateSize() : void
+ {
$width = $this->getWidth();
if (!is_numeric($width) || is_string($width)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getWidth()',
- 'expected' => 'float',
- 'actual' => gettype($width)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getWidth()',
+ 'expected' => 'float',
+ 'actual' => gettype($width)
+ ]
+ )
+ );
}
$height = $this->getHeight();
if (!is_numeric($height) || is_string($height)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getHeight()',
- 'expected' => 'float',
- 'actual' => gettype($height)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getHeight()',
+ 'expected' => 'float',
+ 'actual' => gettype($height)
+ ]
+ )
+ );
}
}
- private function validateMargins() : void {
+ private function validateMargins() : void
+ {
$marginTop = $this->getMarginTop();
if (!is_numeric($marginTop) || is_string($marginTop)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getMarginTop()',
- 'expected' => 'float',
- 'actual' => gettype($marginTop)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getMarginTop()',
+ 'expected' => 'float',
+ 'actual' => gettype($marginTop)
+ ]
+ )
+ );
}
$marginBottom = $this->getMarginBottom();
if (!is_numeric($marginBottom) || is_string($marginBottom)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getMarginBottom()',
- 'expected' => 'float',
- 'actual' => gettype($marginBottom)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getMarginBottom()',
+ 'expected' => 'float',
+ 'actual' => gettype($marginBottom)
+ ]
+ )
+ );
}
$marginLeft = $this->getMarginLeft();
if (!is_numeric($marginLeft) || is_string($marginLeft)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getMarginLeft()',
- 'expected' => 'float',
- 'actual' => gettype($marginLeft)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getMarginLeft()',
+ 'expected' => 'float',
+ 'actual' => gettype($marginLeft)
+ ]
+ )
+ );
}
$marginRight = $this->getMarginRight();
if (!is_numeric($marginRight) || is_string($marginRight)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getMarginRight()',
- 'expected' => 'float',
- 'actual' => gettype($marginRight)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getMarginRight()',
+ 'expected' => 'float',
+ 'actual' => gettype($marginRight)
+ ]
+ )
+ );
}
}
- private function validateSupport() : void {
+ private function validateSupport() : void
+ {
$support1D = $this->getSupport1DBarcode();
if (!is_bool($support1D)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getSupport1DBarcode()',
- 'expected' => 'boolean',
- 'actual' => gettype($support1D)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getSupport1DBarcode()',
+ 'expected' => 'boolean',
+ 'actual' => gettype($support1D)
+ ]
+ )
+ );
}
$support2D = $this->getSupport2DBarcode();
if (!is_bool($support2D)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getSupport2DBarcode()',
- 'expected' => 'boolean',
- 'actual' => gettype($support2D)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getSupport2DBarcode()',
+ 'expected' => 'boolean',
+ 'actual' => gettype($support2D)
+ ]
+ )
+ );
}
$supportFields = $this->getSupportFields();
if (!is_int($supportFields)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getSupportFields()',
- 'expected' => 'integer',
- 'actual' => gettype($supportFields)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getSupportFields()',
+ 'expected' => 'integer',
+ 'actual' => gettype($supportFields)
+ ]
+ )
+ );
}
$supportLogo = $this->getSupportLogo();
if (!is_bool($supportLogo)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getSupportLogo()',
- 'expected' => 'boolean',
- 'actual' => gettype($supportLogo)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getSupportLogo()',
+ 'expected' => 'boolean',
+ 'actual' => gettype($supportLogo)
+ ]
+ )
+ );
}
$supportTitle = $this->getSupportTitle();
if (!is_bool($supportTitle)) {
- throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
- 'name' => 'getSupportTitle()',
- 'expected' => 'boolean',
- 'actual' => gettype($supportTitle)
- ]));
+ throw new \UnexpectedValueException(
+ trans(
+ 'admin/labels/message.invalid_return_type', [
+ 'name' => 'getSupportTitle()',
+ 'expected' => 'boolean',
+ 'actual' => gettype($supportTitle)
+ ]
+ )
+ );
}
}
@@ -539,23 +627,26 @@ abstract class Label
/**
* Public Static Functions
- */
+ */
/**
* Find size of a page by its format.
*
- * @param string $format Format name (eg: 'A4', 'LETTER', etc.)
- * @param string $orientation 'L' for Landscape, 'P' for Portrait ('L' default)
- * @param string $unit Unit of measure to return in ('mm' default)
+ * @param string $format Format name (eg: 'A4', 'LETTER', etc.)
+ * @param string $orientation 'L' for Landscape, 'P' for Portrait ('L' default)
+ * @param string $unit Unit of measure to return in ('mm' default)
*
* @return object (object)[ 'width' => (float)123.4, 'height' => (float)123.4 ]
*/
- public static function fromFormat($format, $orientation='L', $unit='mm', $round=false) {
+ public static function fromFormat($format, $orientation='L', $unit='mm', $round=false)
+ {
$size = collect(TCPDF_STATIC::getPageSizeFromFormat(strtoupper($format)))
->sort()
- ->map(function ($value) use ($unit) {
- return Helper::convertUnit($value, 'pt', $unit);
- })
+ ->map(
+ function ($value) use ($unit) {
+ return Helper::convertUnit($value, 'pt', $unit);
+ }
+ )
->toArray();
$width = ($orientation == 'L') ? $size[1] : $size[0];
$height = ($orientation == 'L') ? $size[0] : $size[1];
@@ -571,16 +662,19 @@ abstract class Label
* Unlike most Models, these are defined by their existence as non-
* abstract classes stored in Models\Labels.
*
- * @param string|Arrayable|array|null $path Label path[s]
+ * @param string|Arrayable|array|null $path Label path[s]
* @return Collection|Label|null
*/
- public static function find($name=null) {
+ public static function find($name=null)
+ {
// Find many
if (is_array($name) || $name instanceof Arrayable) {
$labels = collect($name)
- ->map(function ($thisname) {
- return static::find($thisname);
- })
+ ->map(
+ function ($thisname) {
+ return static::find($thisname);
+ }
+ )
->whereNotNull();
return ($labels->count() > 0) ? $labels : null;
}
@@ -588,26 +682,36 @@ abstract class Label
// Find one
if ($name !== null) {
return static::find()
- ->sole(function ($label) use ($name) {
- return $label->getName() == $name;
- });
+ ->sole(
+ function ($label) use ($name) {
+ return $label->getName() == $name;
+ }
+ );
}
// Find all
return collect(File::allFiles(__DIR__))
- ->map(function ($file) {
- preg_match_all('/\/*(.+?)(?:\/|\.)/', $file->getRelativePathName(), $matches);
- return __NAMESPACE__ . '\\' . implode('\\', $matches[1]);
- })
- ->filter(function ($name) {
- if (!class_exists($name)) return false;
- $refClass = new \ReflectionClass($name);
- if ($refClass->isAbstract()) return false;
- return $refClass->isSubclassOf(Label::class);
- })
- ->map(function ($name) {
- return new $name();
- });
+ ->map(
+ function ($file) {
+ preg_match_all('/\/*(.+?)(?:\/|\.)/', $file->getRelativePathName(), $matches);
+ return __NAMESPACE__ . '\\' . implode('\\', $matches[1]);
+ }
+ )
+ ->filter(
+ function ($name) {
+ if (!class_exists($name)) { return false;
+ }
+ $refClass = new \ReflectionClass($name);
+ if ($refClass->isAbstract()) { return false;
+ }
+ return $refClass->isSubclassOf(Label::class);
+ }
+ )
+ ->map(
+ function ($name) {
+ return new $name();
+ }
+ );
}
diff --git a/app/Models/Labels/RectangleSheet.php b/app/Models/Labels/RectangleSheet.php
index f5fe5cda98..e9c6f007d9 100644
--- a/app/Models/Labels/RectangleSheet.php
+++ b/app/Models/Labels/RectangleSheet.php
@@ -33,9 +33,13 @@ abstract class RectangleSheet extends Sheet
public abstract function getLabelRowSpacing();
- public function getLabelsPerPage() { return $this->getColumns() * $this->getRows(); }
+ public function getLabelsPerPage()
+ {
+ return $this->getColumns() * $this->getRows();
+ }
- public function getLabelPosition($index) {
+ public function getLabelPosition($index)
+ {
$printIndex = $index + $this->getLabelIndexOffset();
$row = (int)($printIndex / $this->getColumns());
$col = $printIndex - ($row * $this->getColumns());
diff --git a/app/Models/Labels/Sheet.php b/app/Models/Labels/Sheet.php
index 83e363591a..63f9da173a 100644
--- a/app/Models/Labels/Sheet.php
+++ b/app/Models/Labels/Sheet.php
@@ -6,12 +6,30 @@ abstract class Sheet extends Label
{
protected int $indexOffset = 0;
- public function getWidth() { return $this->getPageWidth(); }
- public function getHeight() { return $this->getPageHeight(); }
- public function getMarginTop() { return $this->getPageMarginTop(); }
- public function getMarginBottom() { return $this->getPageMarginBottom(); }
- public function getMarginLeft() { return $this->getPageMarginLeft(); }
- public function getMarginRight() { return $this->getPageMarginRight(); }
+ public function getWidth()
+ {
+ return $this->getPageWidth();
+ }
+ public function getHeight()
+ {
+ return $this->getPageHeight();
+ }
+ public function getMarginTop()
+ {
+ return $this->getPageMarginTop();
+ }
+ public function getMarginBottom()
+ {
+ return $this->getPageMarginBottom();
+ }
+ public function getMarginLeft()
+ {
+ return $this->getPageMarginLeft();
+ }
+ public function getMarginRight()
+ {
+ return $this->getPageMarginRight();
+ }
/**
* Returns the page width in getUnit() units
@@ -107,7 +125,7 @@ abstract class Sheet extends Label
/**
* Returns label position based on its index
*
- * @param int $index
+ * @param int $index
*
* @return array [x,y]
*/
@@ -123,10 +141,11 @@ abstract class Sheet extends Label
/**
* Handle the data here. Override for multiple-per-page handling
*
- * @param TCPDF $pdf The TCPDF instance
- * @param Collection $data The data
+ * @param TCPDF $pdf The TCPDF instance
+ * @param Collection $data The data
*/
- public function writeAll($pdf, $data) {
+ public function writeAll($pdf, $data)
+ {
$prevPageNumber = -1;
foreach ($data->toArray() as $recordIndex => $record) {
@@ -170,7 +189,8 @@ abstract class Sheet extends Label
*
* @return string
*/
- public final function getLabelOrientation() {
+ public final function getLabelOrientation()
+ {
return ($this->getLabelWidth() >= $this->getLabelHeight()) ? 'L' : 'P';
}
@@ -179,7 +199,8 @@ abstract class Sheet extends Label
*
* @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ]
*/
- public final function getLabelPrintableArea() {
+ public final function getLabelPrintableArea()
+ {
return (object)[
'x1' => $this->getLabelMarginLeft(),
'y1' => $this->getLabelMarginTop(),
@@ -195,15 +216,20 @@ abstract class Sheet extends Label
*
* @return int
*/
- public function getLabelIndexOffset() { return $this->indexOffset; }
+ public function getLabelIndexOffset()
+ {
+ return $this->indexOffset;
+ }
/**
* Sets label index offset (skip positions)
*
- * @param int $offset
- *
+ * @param int $offset
*/
- public function setLabelIndexOffset(int $offset) { $this->indexOffset = $offset; }
+ public function setLabelIndexOffset(int $offset)
+ {
+ $this->indexOffset = $offset;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/L7162.php b/app/Models/Labels/Sheets/Avery/L7162.php
index e1097db9b2..d0e6c673c7 100644
--- a/app/Models/Labels/Sheets/Avery/L7162.php
+++ b/app/Models/Labels/Sheets/Avery/L7162.php
@@ -31,7 +31,8 @@ abstract class L7162 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
- public function __construct() {
+ public function __construct()
+ {
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 0);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class L7162 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginTop; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginLeft; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginLeft;
+ }
- public function getColumns() { return 2; }
- public function getRows() { return 8; }
+ public function getColumns()
+ {
+ return 2;
+ }
+ public function getRows()
+ {
+ return 8;
+ }
- public function getLabelColumnSpacing() { return $this->columnSpacing; }
- public function getLabelRowSpacing() { return $this->rowSpacing; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->columnSpacing;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->rowSpacing;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelBorder() { return 0; }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/L7162_A.php b/app/Models/Labels/Sheets/Avery/L7162_A.php
index 0b3312ba7c..adfe7f382a 100644
--- a/app/Models/Labels/Sheets/Avery/L7162_A.php
+++ b/app/Models/Labels/Sheets/Avery/L7162_A.php
@@ -14,23 +14,59 @@ class L7162_A extends L7162
private const FIELD_SIZE = 4.60;
private const FIELD_MARGIN = 0.30;
- public function getUnit() { return 'mm'; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
- public function getLabelMarginTop() { return 1.0; }
- public function getLabelMarginBottom() { return 1.0; }
- public function getLabelMarginLeft() { return 1.0; }
- public function getLabelMarginRight() { return 1.0; }
+ public function getLabelMarginTop()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginRight()
+ {
+ return 1.0;
+ }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 4; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 4;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;
diff --git a/app/Models/Labels/Sheets/Avery/L7162_B.php b/app/Models/Labels/Sheets/Avery/L7162_B.php
index 9f24d58c2f..833653cabf 100644
--- a/app/Models/Labels/Sheets/Avery/L7162_B.php
+++ b/app/Models/Labels/Sheets/Avery/L7162_B.php
@@ -17,23 +17,59 @@ class L7162_B extends L7162
private const FIELD_SIZE = 4.20;
private const FIELD_MARGIN = 0.30;
- public function getUnit() { return 'mm'; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
- public function getLabelMarginTop() { return 1.0; }
- public function getLabelMarginBottom() { return 0; }
- public function getLabelMarginLeft() { return 1.0; }
- public function getLabelMarginRight() { return 1.0; }
+ public function getLabelMarginTop()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginRight()
+ {
+ return 1.0;
+ }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return false; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return true; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return false;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return true;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;
diff --git a/app/Models/Labels/Sheets/Avery/L7163.php b/app/Models/Labels/Sheets/Avery/L7163.php
index f143260336..01fe9a78e0 100644
--- a/app/Models/Labels/Sheets/Avery/L7163.php
+++ b/app/Models/Labels/Sheets/Avery/L7163.php
@@ -31,7 +31,8 @@ abstract class L7163 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
- public function __construct() {
+ public function __construct()
+ {
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 0);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class L7163 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginTop; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginLeft; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginLeft;
+ }
- public function getColumns() { return 2; }
- public function getRows() { return 7; }
+ public function getColumns()
+ {
+ return 2;
+ }
+ public function getRows()
+ {
+ return 7;
+ }
- public function getLabelColumnSpacing() { return $this->columnSpacing; }
- public function getLabelRowSpacing() { return $this->rowSpacing; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->columnSpacing;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->rowSpacing;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelBorder() { return 0; }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/L7163_A.php b/app/Models/Labels/Sheets/Avery/L7163_A.php
index 6dc33f64dd..47a44d75db 100644
--- a/app/Models/Labels/Sheets/Avery/L7163_A.php
+++ b/app/Models/Labels/Sheets/Avery/L7163_A.php
@@ -14,23 +14,59 @@ class L7163_A extends L7163
private const FIELD_SIZE = 4.80;
private const FIELD_MARGIN = 0.30;
- public function getUnit() { return 'mm'; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
- public function getLabelMarginTop() { return 1.0; }
- public function getLabelMarginBottom() { return 1.0; }
- public function getLabelMarginLeft() { return 1.0; }
- public function getLabelMarginRight() { return 1.0; }
+ public function getLabelMarginTop()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 1.0;
+ }
+ public function getLabelMarginRight()
+ {
+ return 1.0;
+ }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 4; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 4;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;
diff --git a/app/Models/Labels/Sheets/Avery/_3490.php b/app/Models/Labels/Sheets/Avery/_3490.php
index 2782549fd3..c81c980e11 100644
--- a/app/Models/Labels/Sheets/Avery/_3490.php
+++ b/app/Models/Labels/Sheets/Avery/_3490.php
@@ -31,7 +31,8 @@ abstract class _3490 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
- public function __construct() {
+ public function __construct()
+ {
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _3490 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginTop; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginLeft; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginLeft;
+ }
- public function getColumns() { return 3; }
- public function getRows() { return 10; }
+ public function getColumns()
+ {
+ return 3;
+ }
+ public function getRows()
+ {
+ return 10;
+ }
- public function getLabelColumnSpacing() { return $this->columnSpacing; }
- public function getLabelRowSpacing() { return $this->rowSpacing; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->columnSpacing;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->rowSpacing;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelBorder() { return 0; }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/_3490_A.php b/app/Models/Labels/Sheets/Avery/_3490_A.php
index c558e22292..591f8318aa 100644
--- a/app/Models/Labels/Sheets/Avery/_3490_A.php
+++ b/app/Models/Labels/Sheets/Avery/_3490_A.php
@@ -14,23 +14,59 @@ class _3490_A extends _3490
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
- public function getUnit() { return 'in'; }
+ public function getUnit()
+ {
+ return 'in';
+ }
- public function getLabelMarginTop() { return 0.06; }
- public function getLabelMarginBottom() { return 0.06; }
- public function getLabelMarginLeft() { return 0.06; }
- public function getLabelMarginRight() { return 0.06; }
+ public function getLabelMarginTop()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginRight()
+ {
+ return 0.06;
+ }
- public function getSupportAssetTag() { return false; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return false;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Sheets/Avery/_5267.php b/app/Models/Labels/Sheets/Avery/_5267.php
index f5f2f13557..882c862691 100644
--- a/app/Models/Labels/Sheets/Avery/_5267.php
+++ b/app/Models/Labels/Sheets/Avery/_5267.php
@@ -31,7 +31,8 @@ abstract class _5267 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
- public function __construct() {
+ public function __construct()
+ {
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _5267 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginTop; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginLeft; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginLeft;
+ }
- public function getColumns() { return 4; }
- public function getRows() { return 20; }
+ public function getColumns()
+ {
+ return 4;
+ }
+ public function getRows()
+ {
+ return 20;
+ }
- public function getLabelColumnSpacing() { return $this->columnSpacing; }
- public function getLabelRowSpacing() { return $this->rowSpacing; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->columnSpacing;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->rowSpacing;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelBorder() { return 0; }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/_5267_A.php b/app/Models/Labels/Sheets/Avery/_5267_A.php
index efe0855d5e..1ed6f557f0 100644
--- a/app/Models/Labels/Sheets/Avery/_5267_A.php
+++ b/app/Models/Labels/Sheets/Avery/_5267_A.php
@@ -12,23 +12,59 @@ class _5267_A extends _5267
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
- public function getUnit() { return 'in'; }
+ public function getUnit()
+ {
+ return 'in';
+ }
- public function getLabelMarginTop() { return 0.02; }
- public function getLabelMarginBottom() { return 0.00; }
- public function getLabelMarginLeft() { return 0.04; }
- public function getLabelMarginRight() { return 0.04; }
+ public function getLabelMarginTop()
+ {
+ return 0.02;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0.00;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 0.04;
+ }
+ public function getLabelMarginRight()
+ {
+ return 0.04;
+ }
- public function getSupportAssetTag() { return false; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return false; }
- public function getSupportFields() { return 1; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return false;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return false;
+ }
+ public function getSupportFields()
+ {
+ return 1;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
if ($record->has('barcode1d')) {
diff --git a/app/Models/Labels/Sheets/Avery/_5520.php b/app/Models/Labels/Sheets/Avery/_5520.php
index 00cb0e0687..ae9ff0b039 100644
--- a/app/Models/Labels/Sheets/Avery/_5520.php
+++ b/app/Models/Labels/Sheets/Avery/_5520.php
@@ -31,7 +31,8 @@ abstract class _5520 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
- public function __construct() {
+ public function __construct()
+ {
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _5520 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
- public function getPageWidth() { return $this->pageWidth; }
- public function getPageHeight() { return $this->pageHeight; }
+ public function getPageWidth()
+ {
+ return $this->pageWidth;
+ }
+ public function getPageHeight()
+ {
+ return $this->pageHeight;
+ }
- public function getPageMarginTop() { return $this->pageMarginTop; }
- public function getPageMarginBottom() { return $this->pageMarginTop; }
- public function getPageMarginLeft() { return $this->pageMarginLeft; }
- public function getPageMarginRight() { return $this->pageMarginLeft; }
+ public function getPageMarginTop()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginBottom()
+ {
+ return $this->pageMarginTop;
+ }
+ public function getPageMarginLeft()
+ {
+ return $this->pageMarginLeft;
+ }
+ public function getPageMarginRight()
+ {
+ return $this->pageMarginLeft;
+ }
- public function getColumns() { return 3; }
- public function getRows() { return 10; }
+ public function getColumns()
+ {
+ return 3;
+ }
+ public function getRows()
+ {
+ return 10;
+ }
- public function getLabelColumnSpacing() { return $this->columnSpacing; }
- public function getLabelRowSpacing() { return $this->rowSpacing; }
+ public function getLabelColumnSpacing()
+ {
+ return $this->columnSpacing;
+ }
+ public function getLabelRowSpacing()
+ {
+ return $this->rowSpacing;
+ }
- public function getLabelWidth() { return $this->labelWidth; }
- public function getLabelHeight() { return $this->labelHeight; }
+ public function getLabelWidth()
+ {
+ return $this->labelWidth;
+ }
+ public function getLabelHeight()
+ {
+ return $this->labelHeight;
+ }
- public function getLabelBorder() { return 0; }
+ public function getLabelBorder()
+ {
+ return 0;
+ }
}
?>
\ No newline at end of file
diff --git a/app/Models/Labels/Sheets/Avery/_5520_A.php b/app/Models/Labels/Sheets/Avery/_5520_A.php
index 199566d248..9d2c91502d 100644
--- a/app/Models/Labels/Sheets/Avery/_5520_A.php
+++ b/app/Models/Labels/Sheets/Avery/_5520_A.php
@@ -14,23 +14,59 @@ class _5520_A extends _5520
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
- public function getUnit() { return 'in'; }
+ public function getUnit()
+ {
+ return 'in';
+ }
- public function getLabelMarginTop() { return 0.06; }
- public function getLabelMarginBottom() { return 0.06; }
- public function getLabelMarginLeft() { return 0.06; }
- public function getLabelMarginRight() { return 0.06; }
+ public function getLabelMarginTop()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginRight()
+ {
+ return 0.06;
+ }
- public function getSupportAssetTag() { return false; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return false;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Sheets/Avery/_5520_B.php b/app/Models/Labels/Sheets/Avery/_5520_B.php
index eb6494e0aa..9c44e26bf4 100644
--- a/app/Models/Labels/Sheets/Avery/_5520_B.php
+++ b/app/Models/Labels/Sheets/Avery/_5520_B.php
@@ -15,23 +15,59 @@ class _5520_B extends _5520
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
- public function getUnit() { return 'in'; }
+ public function getUnit()
+ {
+ return 'in';
+ }
- public function getLabelMarginTop() { return 0.06; }
- public function getLabelMarginBottom() { return 0.06; }
- public function getLabelMarginLeft() { return 0.06; }
- public function getLabelMarginRight() { return 0.06; }
+ public function getLabelMarginTop()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginBottom()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginLeft()
+ {
+ return 0.06;
+ }
+ public function getLabelMarginRight()
+ {
+ return 0.06;
+ }
- public function getSupportAssetTag() { return false; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return false; }
- public function getSupportFields() { return 2; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getSupportAssetTag()
+ {
+ return false;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return false;
+ }
+ public function getSupportFields()
+ {
+ return 2;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Brother/TZe_12mm.php b/app/Models/Labels/Tapes/Brother/TZe_12mm.php
index f9196847ce..3cf1f54081 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_12mm.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_12mm.php
@@ -11,9 +11,24 @@ abstract class TZe_12mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
- public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
- public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
- public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
- public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
- public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
+ public function getHeight()
+ {
+ return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
+ }
+ public function getMarginTop()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginBottom()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginLeft()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
+ public function getMarginRight()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php b/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php
index f89cfc5d47..5ff9fcd474 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_12mm_A.php
@@ -8,18 +8,45 @@ class TZe_12mm_A extends TZe_12mm
private const BARCODE_MARGIN = 0.30;
private const TEXT_SIZE_MOD = 1.00;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 50.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return false; }
- public function getSupportFields() { return 1; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return false; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 50.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return false;
+ }
+ public function getSupportFields()
+ {
+ return 1;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return false;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
if ($record->has('barcode1d')) {
diff --git a/app/Models/Labels/Tapes/Brother/TZe_18mm.php b/app/Models/Labels/Tapes/Brother/TZe_18mm.php
index 38c14c7aa4..efcc405bc4 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_18mm.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_18mm.php
@@ -11,9 +11,24 @@ abstract class TZe_18mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
- public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
- public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
- public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
- public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
- public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
+ public function getHeight()
+ {
+ return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
+ }
+ public function getMarginTop()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginBottom()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginLeft()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
+ public function getMarginRight()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Brother/TZe_18mm_A.php b/app/Models/Labels/Tapes/Brother/TZe_18mm_A.php
index 32156f5ee6..fd27a80421 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_18mm_A.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_18mm_A.php
@@ -8,18 +8,45 @@ class TZe_18mm_A extends TZe_18mm
private const BARCODE_MARGIN = 0.30;
private const TEXT_SIZE_MOD = 1.00;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 50.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return false; }
- public function getSupportFields() { return 1; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return false; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 50.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return false;
+ }
+ public function getSupportFields()
+ {
+ return 1;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return false;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
if ($record->has('barcode1d')) {
diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm.php b/app/Models/Labels/Tapes/Brother/TZe_24mm.php
index 3c67bc1614..c9ee228beb 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_24mm.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_24mm.php
@@ -11,9 +11,24 @@ abstract class TZe_24mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
- public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
- public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
- public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
- public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
- public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
+ public function getHeight()
+ {
+ return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
+ }
+ public function getMarginTop()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginBottom()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginLeft()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
+ public function getMarginRight()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php b/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php
index ea4c6c9dfb..4210bb2709 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_24mm_A.php
@@ -13,18 +13,45 @@ class TZe_24mm_A extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 65.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 65.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm_B.php b/app/Models/Labels/Tapes/Brother/TZe_24mm_B.php
index cedf5e0cbd..2ea5697316 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_24mm_B.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_24mm_B.php
@@ -15,18 +15,45 @@ class TZe_24mm_B extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 73.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return true; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 73.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return true;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm_C.php b/app/Models/Labels/Tapes/Brother/TZe_24mm_C.php
index 65b3676bfc..e100dfa81b 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_24mm_C.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_24mm_C.php
@@ -15,18 +15,45 @@ class TZe_24mm_C extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 34.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return false; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 0; }
- public function getSupportLogo() { return true; }
- public function getSupportTitle() { return false; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 34.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return false;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 0;
+ }
+ public function getSupportLogo()
+ {
+ return true;
+ }
+ public function getSupportTitle()
+ {
+ return false;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Brother/TZe_24mm_D.php b/app/Models/Labels/Tapes/Brother/TZe_24mm_D.php
index 990a615e53..c88e84d246 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_24mm_D.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_24mm_D.php
@@ -14,18 +14,45 @@ class TZe_24mm_D extends TZe_24mm
private const FIELD_MARGIN = 0.35;
private const BARCODE1D_SIZE = 3.00; // Size for the C128 barcode at bottom
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 65.0; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 65.0;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape.php b/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape.php
index 2069927a34..1f5ee1df3d 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape.php
@@ -14,10 +14,28 @@ abstract class TZe_62mm_Landscape extends Label
private const MARGIN_SIDES = 1.50;
private const MARGIN_ENDS = 1.50;
- public function getWidth() { return Helper::convertUnit(self::WIDTH, 'mm', $this->getUnit()); }
- public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
- public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
- public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
- public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
- public function getRotation() { return 90; }
+ public function getWidth()
+ {
+ return Helper::convertUnit(self::WIDTH, 'mm', $this->getUnit());
+ }
+ public function getMarginTop()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginBottom()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
+ }
+ public function getMarginLeft()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
+ public function getMarginRight()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
+ }
+ public function getRotation()
+ {
+ return 90;
+ }
}
diff --git a/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape_A.php b/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape_A.php
index 3a4d6da2c7..6cf53f8e18 100644
--- a/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape_A.php
+++ b/app/Models/Labels/Tapes/Brother/TZe_62mm_Landscape_A.php
@@ -4,14 +4,38 @@ namespace App\Models\Labels\Tapes\Brother;
class TZe_62mm_Landscape_A extends TZe_62mm_Landscape
{
- public function getUnit() { return 'mm'; }
- public function getHeight() { return 31.50; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 2; }
- public function getSupportLogo() { return true; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getHeight()
+ {
+ return 31.50;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 2;
+ }
+ public function getSupportLogo()
+ {
+ return true;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
private const BARCODE1D_HEIGHT = 3.00;
private const BARCODE1D_MARGIN = 3.00;
@@ -27,9 +51,12 @@ class TZe_62mm_Landscape_A extends TZe_62mm_Landscape
private const FIELD_SIZE = 3.00;
private const FIELD_MARGIN = 0.10;
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter.php b/app/Models/Labels/Tapes/Dymo/LabelWriter.php
index fa427fd213..8d9e37555d 100644
--- a/app/Models/Labels/Tapes/Dymo/LabelWriter.php
+++ b/app/Models/Labels/Tapes/Dymo/LabelWriter.php
@@ -11,9 +11,24 @@ abstract class LabelWriter extends Label
private const MARGIN_SIDES = 0.1;
private const MARGIN_ENDS = 0.1;
- public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'in', $this->getUnit()); }
- public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit()); }
- public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit());}
- public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit()); }
- public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit()); }
+ public function getHeight()
+ {
+ return Helper::convertUnit(self::HEIGHT, 'in', $this->getUnit());
+ }
+ public function getMarginTop()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit());
+ }
+ public function getMarginBottom()
+ {
+ return Helper::convertUnit(self::MARGIN_SIDES, 'in', $this->getUnit());
+ }
+ public function getMarginLeft()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit());
+ }
+ public function getMarginRight()
+ {
+ return Helper::convertUnit(self::MARGIN_ENDS, 'in', $this->getUnit());
+ }
}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php
index 9f5fa735e4..63c270e865 100644
--- a/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php
+++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_1933081.php
@@ -14,19 +14,49 @@ class LabelWriter_1933081 extends LabelWriter
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 89; }
- public function getHeight() { return 25; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 5; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 89;
+ }
+ public function getHeight()
+ {
+ return 25;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 5;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php
index 117486a8e5..63701d31a1 100644
--- a/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php
+++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_2112283.php
@@ -14,19 +14,49 @@ class LabelWriter_2112283 extends LabelWriter
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 54; }
- public function getHeight() { return 25; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 5; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 54;
+ }
+ public function getHeight()
+ {
+ return 25;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 5;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php
index ed8074547b..8c3f050242 100644
--- a/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php
+++ b/app/Models/Labels/Tapes/Dymo/LabelWriter_30252.php
@@ -16,18 +16,45 @@ class LabelWriter_30252 extends LabelWriter
- public function getUnit() { return 'mm'; }
- public function getWidth() { return 96.52; }
- public function getSupportAssetTag() { return true; }
- public function getSupport1DBarcode() { return true; }
- public function getSupport2DBarcode() { return true; }
- public function getSupportFields() { return 3; }
- public function getSupportLogo() { return false; }
- public function getSupportTitle() { return true; }
+ public function getUnit()
+ {
+ return 'mm';
+ }
+ public function getWidth()
+ {
+ return 96.52;
+ }
+ public function getSupportAssetTag()
+ {
+ return true;
+ }
+ public function getSupport1DBarcode()
+ {
+ return true;
+ }
+ public function getSupport2DBarcode()
+ {
+ return true;
+ }
+ public function getSupportFields()
+ {
+ return 3;
+ }
+ public function getSupportLogo()
+ {
+ return false;
+ }
+ public function getSupportTitle()
+ {
+ return true;
+ }
- public function preparePDF($pdf) {}
+ public function preparePDF($pdf)
+ {
+ }
- public function write($pdf, $record) {
+ public function write($pdf, $record)
+ {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
diff --git a/app/Models/Labels/Tapes/Generic/Continuous_53mm.php b/app/Models/Labels/Tapes/Generic/Continuous_53mm.php
new file mode 100644
index 0000000000..18b12fbc99
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Continuous_53mm.php
@@ -0,0 +1,110 @@
+tapeHeight = $height;
+ }
+
+ public function getBarcodeRatio()
+ {
+ return 0.9; // Barcode should use 90% of available width
+ }
+
+ /**
+ * Calculate the required height for the content
+ *
+ * @param $record The record to calculate height for
+ * @return float The calculated height in mm
+ */
+ protected function calculateRequiredHeight($record)
+ {
+ $height = $this->marginTop + $this->marginBottom;
+
+ // Add title height if present
+ if ($record->has('title') && $this->getSupportTitle()) {
+ $height += $this->titleSize + $this->titleMargin;
+ }
+
+ // Add barcode height if present
+ if (($record->has('barcode2d') && $this->getSupport2DBarcode())
+ || ($record->has('barcode') && $this->getSupport1DBarcode())
+ ) {
+ $pa = $this->getPrintableArea();
+ $usableWidth = $pa->w;
+ $barcodeSize = $usableWidth * $this->getBarcodeRatio();
+ $height += $barcodeSize + $this->barcodeMargin;
+ }
+
+ // Add fields height if present
+ if ($record->has('fields') && $this->getSupportFields() > 0) {
+ foreach ($record->get('fields') as $field) {
+ $height += $this->labelSize + $this->labelMargin;
+ $height += $this->fieldSize + $this->fieldMargin;
+ }
+ }
+
+ // Add a small buffer to ensure everything fits
+ $height += 2.0;
+
+ // Ensure minimum height
+ return max($this->minHeight, $height);
+ }
+
+ /**
+ * Override the writeAll method to support dynamic page sizes for continuous tapes
+ */
+ public function writeAll($pdf, $data)
+ {
+ // Use auto-sizing for continuous tapes, fixed height for die-cut tapes
+ if ($this->continuous) {
+ $data->each(
+ function ($record, $index) use ($pdf) {
+ // Calculate the required height for this record
+ $requiredHeight = $this->calculateRequiredHeight($record);
+
+ // Temporarily update the height property
+ $originalHeight = $this->height;
+ $this->height = $requiredHeight;
+
+ // Add a new page with the calculated dimensions
+ $pdf->AddPage(
+ $this->getOrientation(),
+ [$this->getWidth(), $requiredHeight],
+ false, // Don't reset page number
+ false // Don't reset object ID
+ );
+
+ // Write the content
+ $this->write($pdf, $record);
+
+ // Restore the original height
+ $this->height = $originalHeight;
+ }
+ );
+ } else {
+ // Use the default implementation for non-continuous (die-cut) tapes
+ parent::writeAll($pdf, $data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/Continuous_53mm_A.php b/app/Models/Labels/Tapes/Generic/Continuous_53mm_A.php
new file mode 100644
index 0000000000..4f40f64627
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Continuous_53mm_A.php
@@ -0,0 +1,96 @@
+SetAutoPageBreak(false);
+ }
+
+ public function write($pdf, $record)
+ {
+ $pa = $this->getPrintableArea();
+
+ $currentX = $pa->x1;
+ $currentY = $pa->y1;
+ $usableWidth = $pa->w;
+ $usableHeight = $pa->h;
+
+ if ($record->has('title')) {
+ static::writeText(
+ $pdf, $record->get('title'),
+ $pa->x1, $pa->y1,
+ 'freesans', '', $this->titleSize, 'C',
+ $pa->w, $this->titleSize, true, 0
+ );
+ $currentY += $this->titleSize + $this->titleMargin;
+ $usableHeight -= $this->titleSize + $this->titleMargin;
+ }
+
+ // Make the barcode as large as possible while still leaving room for fields
+ $barcodeSize = min($usableHeight * 0.8, $usableWidth * $this->getBarcodeRatio());
+
+ if ($record->has('barcode2d')) {
+ $barcodeX = $pa->x1 + ($usableWidth - $barcodeSize) / 2;
+
+ static::write2DBarcode(
+ $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
+ $barcodeX, $currentY,
+ $barcodeSize, $barcodeSize
+ );
+ $currentY += $barcodeSize + $this->barcodeMargin;
+ }
+
+ if ($record->has('fields')) {
+ foreach ($record->get('fields') as $field) {
+ static::writeText(
+ $pdf, $field['label'],
+ $currentX, $currentY,
+ 'freesans', '', $this->labelSize, 'L',
+ $usableWidth, $this->labelSize, true, 0
+ );
+ $currentY += $this->labelSize + $this->labelMargin;
+
+ static::writeText(
+ $pdf, $field['value'],
+ $currentX, $currentY,
+ 'freemono', 'B', $this->fieldSize, 'L',
+ $usableWidth, $this->fieldSize, true, 0, 0.01
+ );
+ $currentY += $this->fieldSize + $this->fieldMargin;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in.php b/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in.php
new file mode 100644
index 0000000000..47856823bc
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in.php
@@ -0,0 +1,178 @@
+tapeHeight = $length;
+
+ $this->marginTop = 0.1;
+ $this->marginBottom = 0.1;
+ // Keep small horizontal margins
+ $this->marginLeft = self::TAPE_WIDTH * 0.2;
+ // $this->marginRight = self::TAPE_WIDTH * 0.1;
+
+ // Override font sizes to make them larger
+ // Calculate a larger base font size (3x the default)
+ $baseFontSize = self::TAPE_WIDTH * 0.16; // 3x the default 0.07
+
+ // Recalculate all element sizing based on the larger base font size
+ $this->titleSize = $baseFontSize; // Same as base font size
+ $this->titleMargin = $baseFontSize * 0.3; // 30% of base font size
+ $this->fieldSize = $baseFontSize * 1.1; // 110% of base font size
+ $this->fieldMargin = $baseFontSize * 0.1; // 10% of base font size
+ $this->labelSize = $baseFontSize * 0.7; // 70% of base font size
+ $this->labelMargin = $baseFontSize * -0.1; // -10% of base font size
+ $this->barcodeMargin = $baseFontSize * 0.9; // 20% of base font size
+ $this->tagSize = $baseFontSize * 0.8; // 80% of base font size
+ }
+
+ public function getBarcodeRatio()
+ {
+ return 1.0; // Barcode should use 100% of available height
+ }
+
+ /**
+ * Calculate the required length for the content
+ *
+ * @param $record The record to calculate length for
+ * @return float The calculated length in inches
+ */
+ protected function calculateRequiredLength($record)
+ {
+
+ // Calculate length needed for barcode and fields side by side
+ $requiredLength = 0;
+
+ // Add barcode length if present
+ if (($record->has('barcode2d') && $this->getSupport2DBarcode())
+ || ($record->has('barcode') && $this->getSupport1DBarcode())
+ ) {
+ // Use full tape width for barcode size
+ $barcodeSize = self::TAPE_WIDTH;
+ $requiredLength += $barcodeSize + $this->barcodeMargin * 0.3; // Minimal margin
+ }
+
+ // Add fields length if present - calculate based on actual content
+ if ($record->has('fields') && $this->getSupportFields() > 0) {
+ $fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
+
+ // Base width for field area
+ $fieldsWidth = self::TAPE_WIDTH;
+
+ // Calculate additional width based on text length
+ foreach ($fields as $field) {
+ // Get label and value text
+ $labelText = $field['label'] ?? '';
+ $valueText = $field['value'] ?? '';
+
+ // Calculate approximate width needed based on text length
+ // Increase character width to ensure enough space (0.15 inches per character)
+ $labelWidth = strlen($labelText) * 0.09;
+ $valueWidth = strlen($valueText) * 0.09;
+
+ // Use the longer of the two
+ $textWidth = max($labelWidth, $valueWidth);
+
+ // Ensure minimum width and add to total
+ $fieldsWidth = max($fieldsWidth, $textWidth);
+ }
+
+ // Add the calculated width for fields
+ $requiredLength += $fieldsWidth;
+
+ // Add minimal extra space for field padding
+ // Reduce padding to eliminate extraneous space on right edge
+ // $requiredLength += self::TAPE_WIDTH * 0.1;
+ }
+
+ // Ensure minimum length
+ return max($this->minHeight, $requiredLength);
+ }
+
+ /**
+ * Calculate text width accurately using the PDF object
+ *
+ * @param $pdf The PDF object
+ * @param string $text The text to measure
+ * @param string $font The font to use
+ * @param string $style The font style
+ * @param float $size The font size
+ * @return float The calculated width
+ */
+ protected function calculateTextWidth($pdf, $text, $font, $style, $size)
+ {
+ $originalFont = $pdf->getFontFamily();
+ $originalStyle = $pdf->getFontStyle();
+ $originalSize = $pdf->getFontSizePt();
+
+ $pdf->SetFont($font, $style, Helper::convertUnit($size, $this->getUnit(), 'pt', true));
+ $width = $pdf->GetStringWidth($text);
+
+ // Restore original font settings
+ $pdf->SetFont($originalFont, $originalStyle, $originalSize);
+
+ return $width;
+ }
+
+ /**
+ * Override the writeAll method to support dynamic page sizes for continuous tapes
+ */
+ public function writeAll($pdf, $data)
+ {
+ // Use auto-sizing for continuous tapes, fixed height for die-cut tapes
+ if ($this->continuous) {
+ $data->each(
+ function ($record, $index) use ($pdf) {
+ // Calculate the required length by calling write with calculateOnly=true
+ $requiredLength = $this->write($pdf, $record);
+
+ // If write didn't return a length (old implementation), fall back to calculateRequiredLength
+ if ($requiredLength === null) {
+ $requiredLength = $this->calculateRequiredLength($record);
+ }
+
+ // Temporarily update the height property
+ $originalHeight = $this->height;
+ $this->height = self::TAPE_WIDTH; // Keep height fixed at tape width
+
+ // Add a new page with the calculated dimensions
+ // Keep height fixed at TAPE_WIDTH, use calculated length for width
+ $pdf->AddPage(
+ $this->getOrientation(),
+ [$requiredLength, self::TAPE_WIDTH],
+ false, // Don't reset page number
+ false // Don't reset object ID
+ );
+
+ // Write the content
+ $this->write($pdf, $record);
+
+ // Restore the original height
+ $this->height = $originalHeight;
+ }
+ );
+ } else {
+ // Use the default implementation for non-continuous (die-cut) tapes
+ parent::writeAll($pdf, $data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in_A.php b/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in_A.php
new file mode 100644
index 0000000000..23325aa837
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Continuous_Landscape_0_59in_A.php
@@ -0,0 +1,180 @@
+SetAutoPageBreak(false);
+ }
+
+ public function write($pdf, $record, $calculateOnly = false)
+ {
+ $pa = $this->getPrintableArea();
+
+ $currentX = $pa->x1;
+ $currentY = $pa->y1;
+ $usableWidth = $pa->w;
+ $usableHeight = $pa->h;
+
+ // Calculate required length based on content
+ $requiredLength = 0;
+
+ // Use full usable height for barcode
+ $barcodeSize = $usableHeight;
+
+ // Add barcode width to required length
+ if ($record->has('barcode2d') && $this->getSupport2DBarcode()) {
+ $requiredLength += $barcodeSize;
+ // Add gap between barcode and fields
+ $requiredLength += $this->barcodeMargin;
+ }
+
+ // Calculate fields width using accurate text measurement
+ if ($record->has('fields') && $this->getSupportFields() > 0) {
+ $fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
+ $fieldsWidth = 0;
+
+ foreach ($fields as $field) {
+ $labelText = $field['label'] ?? '';
+ $valueText = $field['value'] ?? '';
+
+ // Calculate accurate width using the PDF object
+ $labelWidth = $this->calculateTextWidth($pdf, $labelText, 'freesans', 'B', $this->labelSize * 1.2);
+ $valueWidth = $this->calculateTextWidth($pdf, $valueText, 'freemono', 'B', $this->fieldSize * 1.3);
+
+ // Use the longer of the two
+ $textWidth = max($labelWidth, $valueWidth);
+ $fieldsWidth = max($fieldsWidth, $textWidth);
+ }
+
+ $requiredLength += $fieldsWidth;
+ }
+
+ // Add more padding to prevent text from being cut off
+ // $requiredLength += self::TAPE_WIDTH * 0.8;
+
+ // Ensure minimum length
+ $requiredLength = max($this->minHeight, $requiredLength);
+
+ // If we're just calculating, return the length
+ if ($calculateOnly) {
+ return $requiredLength;
+ }
+
+ // Otherwise, render the content
+ // Position barcode on the left side
+ if ($record->has('barcode2d') && $this->getSupport2DBarcode()) {
+ // Position at top of usable area
+ static::write2DBarcode(
+ $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
+ $currentX, $currentY,
+ $barcodeSize, $barcodeSize
+ );
+ $currentX += $barcodeSize + $this->barcodeMargin;
+ $usableWidth -= $barcodeSize + $this->barcodeMargin;
+ }
+
+ // Position fields to the right of the barcode
+ if ($record->has('fields') && $this->getSupportFields() > 0) {
+ // Limit to the number of supported fields
+ $fields = array_slice($record->get('fields')->toArray(), 0, $this->getSupportFields());
+
+ // Calculate total height needed for fields
+ $totalFieldsHeight = 0;
+ foreach ($fields as $field) {
+ $totalFieldsHeight += $this->labelSize * 1.2 + $this->labelMargin; // Increased label size by 20%
+ $totalFieldsHeight += $this->fieldSize * 1.3 + $this->fieldMargin * 2; // Increased field size by 30% and margin
+ }
+
+ // Start position - respect top margin
+ $fieldY = $currentY; // $currentY already includes the top margin
+ $fieldWidth = $usableWidth;
+
+ // Calculate available height for fields (respecting margins)
+ $availableHeight = $usableHeight;
+
+ // If fields don't fill available height, adjust spacing proportionally
+ // but don't exceed the available height
+ $scaleFactor = 1.0; // Default scale factor
+ if ($totalFieldsHeight < $availableHeight && count($fields) > 0) {
+ // Scale up to fill available height, but not too much
+ $scaleFactor = min(1.5, $availableHeight / $totalFieldsHeight);
+ } else if ($totalFieldsHeight > $availableHeight && count($fields) > 0) {
+ // Scale down to fit within available height
+ $scaleFactor = $availableHeight / $totalFieldsHeight;
+ }
+
+ foreach ($fields as $field) {
+ // Calculate scaled spacing
+ $labelHeight = $this->labelSize * 1.2 * $scaleFactor;
+ $labelSpacing = $this->labelMargin * $scaleFactor;
+ $fieldHeight = $this->fieldSize * 1.3 * $scaleFactor;
+ $fieldSpacing = $this->fieldMargin * 2 * $scaleFactor;
+
+ // Check if label is empty or null
+ $labelText = $field['label'] ?? '';
+ $valueText = $field['value'] ?? '';
+
+ if (empty(trim($labelText))) {
+ // If label is empty, just render the value at the current Y position
+ static::writeText(
+ $pdf, $valueText,
+ $currentX, $fieldY,
+ 'freemono', 'B', $this->fieldSize * 1.3, 'L', // Increased field size by 30%
+ $fieldWidth, $fieldHeight, false, 0, 0.00
+ );
+ $fieldY += ($fieldHeight + $fieldSpacing) + 0.02; // Increased spacing after value
+ } else {
+ // If label has content, render both label and value
+ static::writeText(
+ $pdf, $labelText,
+ $currentX, $fieldY,
+ 'freesans', 'B', $this->labelSize * 1.2, 'L', // Increased label size by 20% and made bold
+ $labelWidth, $labelHeight, false, 0,
+ );
+ $fieldY += ($labelHeight + $labelSpacing) + 0.01;
+
+ // Value
+ static::writeText(
+ $pdf, $valueText,
+ $currentX, $fieldY, // Position value directly below label
+ 'freemono', 'B', $this->fieldSize * 1.3, 'L', // Increased field size by 30%
+ $fieldWidth, $fieldHeight, false, 0, 0.00
+ );
+ $fieldY += ($fieldHeight + $fieldSpacing) + 0.02; // Increased spacing after value
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/GenericTape.php b/app/Models/Labels/Tapes/Generic/GenericTape.php
new file mode 100644
index 0000000000..1c8426a79a
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/GenericTape.php
@@ -0,0 +1,127 @@
+width = $width;
+ $this->height = $height;
+ $this->continuous = $continuous;
+ $this->spacing = $spacing;
+
+ // Calculate base font size (7% of tape width)
+ $baseFontSize = static::TAPE_WIDTH * 0.07;
+
+ // Calculate margin (4% of tape width)
+ $margin = static::TAPE_WIDTH * 0.04;
+
+ // Set margins
+ $this->marginTop = $margin;
+ $this->marginBottom = $margin;
+ $this->marginLeft = $margin;
+ $this->marginRight = $margin;
+
+ // Calculate and set element sizing based on base font size
+ $this->titleSize = $baseFontSize; // Same as base font size
+ $this->titleMargin = $baseFontSize * 0.3; // 30% of base font size
+ $this->fieldSize = $baseFontSize * 1.1; // 110% of base font size
+ $this->fieldMargin = $baseFontSize * 0.1; // 10% of base font size
+ $this->labelSize = $baseFontSize * 0.7; // 70% of base font size
+ $this->labelMargin = $baseFontSize * -0.1; // -10% of base font size
+ $this->barcodeMargin = $baseFontSize * 0.5; // 50% of base font size
+ $this->tagSize = $baseFontSize * 0.8; // 80% of base font size
+ }
+
+ // Unit of measurement
+ public function getUnit()
+ {
+ return 'mm';
+ }
+
+ // Label dimensions
+ public function getWidth()
+ {
+ return $this->width;
+ }
+ public function getHeight()
+ {
+ return $this->height;
+ }
+
+ // Margins
+ public function getMarginTop()
+ {
+ return $this->marginTop;
+ }
+ public function getMarginBottom()
+ {
+ return $this->marginBottom;
+ }
+ public function getMarginLeft()
+ {
+ return $this->marginLeft;
+ }
+ public function getMarginRight()
+ {
+ return $this->marginRight;
+ }
+
+
+ /**
+ * Check if this is a continuous tape
+ *
+ * @return bool
+ */
+ public function isContinuous()
+ {
+ return $this->continuous;
+ }
+
+ /**
+ * Get spacing between labels (for die-cut tapes)
+ *
+ * @return float
+ */
+ public function getSpacing()
+ {
+ return $this->spacing;
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/Tape_53mm.php b/app/Models/Labels/Tapes/Generic/Tape_53mm.php
new file mode 100644
index 0000000000..679380f221
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Tape_53mm.php
@@ -0,0 +1,34 @@
+tapeHeight = $height;
+ }
+
+ /**
+ * Get the barcode size ratio for calculations
+ *
+ * @return float
+ */
+ public function getBarcodeRatio()
+ {
+ return 0.9; // Barcode should use 90% of available width
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Labels/Tapes/Generic/Tape_53mm_A.php b/app/Models/Labels/Tapes/Generic/Tape_53mm_A.php
new file mode 100644
index 0000000000..8b7a79dda0
--- /dev/null
+++ b/app/Models/Labels/Tapes/Generic/Tape_53mm_A.php
@@ -0,0 +1,101 @@
+SetAutoPageBreak(false);
+ }
+
+ public function write($pdf, $record)
+ {
+ $pa = $this->getPrintableArea();
+
+ $currentX = $pa->x1;
+ $currentY = $pa->y1;
+ $usableWidth = $pa->w;
+ $usableHeight = $pa->h;
+
+ if ($record->has('title')) {
+ static::writeText(
+ $pdf, $record->get('title'),
+ $pa->x1, $pa->y1,
+ 'freesans', '', $this->titleSize, 'C',
+ $pa->w, $this->titleSize, true, 0
+ );
+ $currentY += $this->titleSize + $this->titleMargin;
+ $usableHeight -= $this->titleSize + $this->titleMargin;
+ }
+
+ // Make the barcode as large as possible while still leaving room for fields
+ $barcodeSize = min($usableHeight * 0.8, $usableWidth * $this->getBarcodeRatio());
+
+ if ($record->has('barcode2d')) {
+ $barcodeX = $pa->x1 + ($usableWidth - $barcodeSize) / 2;
+
+ static::write2DBarcode(
+ $pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
+ $barcodeX, $currentY,
+ $barcodeSize, $barcodeSize
+ );
+ $currentY += $barcodeSize + $this->barcodeMargin;
+ }
+
+ if ($record->has('fields')) {
+ foreach ($record->get('fields') as $field) {
+ static::writeText(
+ $pdf, $field['label'],
+ $currentX, $currentY,
+ 'freesans', '', $this->labelSize, 'L',
+ $usableWidth, $this->labelSize, true, 0
+ );
+ $currentY += $this->labelSize + $this->labelMargin;
+
+ static::writeText(
+ $pdf, $field['value'],
+ $currentX, $currentY,
+ 'freemono', 'B', $this->fieldSize, 'L',
+ $usableWidth, $this->fieldSize, true, 0, 0.01
+ );
+ $currentY += $this->fieldSize + $this->fieldMargin;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Ldap.php b/app/Models/Ldap.php
index f71f926a93..e99ae07265 100644
--- a/app/Models/Ldap.php
+++ b/app/Models/Ldap.php
@@ -31,7 +31,7 @@ class Ldap extends Model
* Makes a connection to LDAP using the settings in Admin > Settings.
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
* @return connection
*/
public static function connectToLdap()
@@ -81,10 +81,10 @@ class Ldap extends Model
* Binds/authenticates the user to LDAP, and returns their attributes.
*
* @author [A. Gianotto] []
- * @since [v3.0]
- * @param $username
- * @param $password
- * @param bool|false $user
+ * @since [v3.0]
+ * @param $username
+ * @param $password
+ * @param bool|false $user
* @return bool true if the username and/or password provided are valid
* false if the username and/or password provided are invalid
* array of ldap_attributes if $user is true
@@ -160,8 +160,8 @@ class Ldap extends Model
* Here we also return a better error if the app key is donked.
*
* @author [A. Gianotto] []
- * @since [v3.0]
- * @param bool|false $user
+ * @since [v3.0]
+ * @param bool|false $user
* @return bool true if the username and/or password provided are valid
* false if the username and/or password provided are invalid
*/
@@ -169,37 +169,37 @@ class Ldap extends Model
{
$ldap_username = Setting::getSettings()->ldap_uname;
- if ( $ldap_username ) {
- // Lets return some nicer messages for users who donked their app key, and disable LDAP
- try {
- $ldap_pass = Crypt::decrypt(Setting::getSettings()->ldap_pword);
- } catch (Exception $e) {
- throw new Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
- }
+ if ($ldap_username ) {
+ // Lets return some nicer messages for users who donked their app key, and disable LDAP
+ try {
+ $ldap_pass = Crypt::decrypt(Setting::getSettings()->ldap_pword);
+ } catch (Exception $e) {
+ throw new Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
+ }
- if (! $ldapbind = @ldap_bind($connection, $ldap_username, $ldap_pass)) {
- throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
- }
- // TODO - this just "falls off the end" but the function states that it should return true or false
- // unfortunately, one of the use cases for this function is wrong and *needs* for that failure mode to fire
- // so I don't want to fix this right now.
- // this method MODIFIES STATE on the passed-in $connection and just returns true or false (or, in this case, undefined)
- // at the next refactor, this should be appropriately modified to be more consistent.
- } else {
- // LDAP should also work with anonymous bind (no dn, no password available)
- if (! $ldapbind = @ldap_bind($connection )) {
- throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
- }
- }
- }
+ if (! $ldapbind = @ldap_bind($connection, $ldap_username, $ldap_pass)) {
+ throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
+ }
+ // TODO - this just "falls off the end" but the function states that it should return true or false
+ // unfortunately, one of the use cases for this function is wrong and *needs* for that failure mode to fire
+ // so I don't want to fix this right now.
+ // this method MODIFIES STATE on the passed-in $connection and just returns true or false (or, in this case, undefined)
+ // at the next refactor, this should be appropriately modified to be more consistent.
+ } else {
+ // LDAP should also work with anonymous bind (no dn, no password available)
+ if (! $ldapbind = @ldap_bind($connection)) {
+ throw new Exception('Could not bind to LDAP: '.ldap_error($connection));
+ }
+ }
+ }
/**
* Parse and map LDAP attributes based on settings
*
* @author [A. Gianotto] []
- * @since [v3.0]
+ * @since [v3.0]
*
- * @param $ldapatttibutes
+ * @param $ldapatttibutes
* @return array|bool
*/
public static function parseAndMapLdapAttributes($ldapattributes)
@@ -238,8 +238,8 @@ class Ldap extends Model
* Create user from LDAP attributes
*
* @author [A. Gianotto] []
- * @since [v3.0]
- * @param $ldapatttibutes
+ * @since [v3.0]
+ * @param $ldapatttibutes
* @return User | bool
*/
public static function createUserFromLdap($ldapatttibutes, $password)
@@ -279,11 +279,11 @@ class Ldap extends Model
* Searches LDAP
*
* @author [A. Gianotto] []
- * @since [v3.0]
- * @param $base_dn
- * @param $count
- * @param $filter
- * @param $attributes
+ * @since [v3.0]
+ * @param $base_dn
+ * @param $count
+ * @param $filter
+ * @param $attributes
* @return array|bool
*/
public static function findLdapUsers($base_dn = null, $count = -1, $filter = null, $attributes = [])
@@ -331,7 +331,7 @@ class Ldap extends Model
$errmsg = null;
$referrals = null;
$controls = [];
- ldap_parse_result($ldapconn, $search_results, $errcode , $matcheddn , $errmsg , $referrals, $controls);
+ ldap_parse_result($ldapconn, $search_results, $errcode, $matcheddn, $errmsg, $referrals, $controls);
if (isset($controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'])) {
// You need to pass the cookie from the last call to the next one
$cookie = $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'];
diff --git a/app/Models/License.php b/app/Models/License.php
index 0997c1e57b..a40728a832 100755
--- a/app/Models/License.php
+++ b/app/Models/License.php
@@ -3,6 +3,7 @@
namespace App\Models;
use App\Helpers\Helper;
+use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Carbon\Carbon;
@@ -21,6 +22,7 @@ class License extends Depreciable
use SoftDeletes;
use CompanyableTrait;
+ use HasUploads;
use Loggable, Presentable;
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
@@ -43,7 +45,7 @@ class License extends Depreciable
protected $rules = [
'name' => 'required|string|min:3|max:255',
- 'seats' => 'required|min:1|integer',
+ 'seats' => 'required|min:1|integer|limit_change:10000', // limit_change is a "pseudo-rule" that translates into 'between', see prepareLimitChangeRule() below
'license_email' => 'email|nullable|max:120',
'license_name' => 'string|nullable|max:100',
'notes' => 'string|nullable',
@@ -113,6 +115,7 @@ class License extends Depreciable
'company' => ['name'],
'category' => ['name'],
'depreciation' => ['name'],
+ 'supplier' => ['name'],
];
protected $appends = ['free_seat_count'];
@@ -120,39 +123,51 @@ class License extends Depreciable
* Update seat counts when the license is updated
*
* @author A. Gianotto
- * @since [v3.0]
+ * @since [v3.0]
*/
public static function boot()
{
parent::boot();
// We need to listen for created for the initial setup so that we have a license ID.
- static::created(function ($license) {
- $newSeatCount = $license->getAttributes()['seats'];
+ static::created(
+ function ($license) {
+ $newSeatCount = $license->getAttributes()['seats'];
- return static::adjustSeatCount($license, 0, $newSeatCount);
- });
+ return static::adjustSeatCount($license, 0, $newSeatCount);
+ }
+ );
// However, we listen for updating to be able to prevent the edit if we cannot delete enough seats.
- static::updating(function ($license) {
- $newSeatCount = $license->getAttributes()['seats'];
- //$oldSeatCount = isset($license->getOriginal()['seats']) ? $license->getOriginal()['seats'] : 0;
- /*
- That previous method *did* mostly work, but if you ever managed to get your $license->seats value out of whack
- with your actual count of license_seats *records*, you would never manage to get back 'into whack'.
- The below method actually grabs a count of existing license_seats records, so it will be more accurate.
- This means that if your license_seats are out of whack, you can change the quantity and hit 'save' and it
- will manage to 'true up' and make your counts line up correctly.
- */
- $oldSeatCount = $license->license_seats_count;
+ static::updating(
+ function ($license) {
+ $newSeatCount = $license->getAttributes()['seats'];
+ //$oldSeatCount = isset($license->getOriginal()['seats']) ? $license->getOriginal()['seats'] : 0;
+ /*
+ That previous method *did* mostly work, but if you ever managed to get your $license->seats value out of whack
+ with your actual count of license_seats *records*, you would never manage to get back 'into whack'.
+ The below method actually grabs a count of existing license_seats records, so it will be more accurate.
+ This means that if your license_seats are out of whack, you can change the quantity and hit 'save' and it
+ will manage to 'true up' and make your counts line up correctly.
+ */
+ $oldSeatCount = $license->license_seats_count;
- return static::adjustSeatCount($license, $oldSeatCount, $newSeatCount);
- });
+ return static::adjustSeatCount($license, $oldSeatCount, $newSeatCount);
+ }
+ );
+ }
+
+ public function prepareLimitChangeRule($parameters, $field)
+ {
+ $actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
+ $lower_bound = $actual_seat_count - $parameters[0];
+ $upper_bound = $actual_seat_count + $parameters[0];
+ return ["between", ($lower_bound <= 0 ? 1 : $lower_bound), $upper_bound];
}
/**
* Balance seat counts
*
* @author A. Gianotto
- * @since [v3.0]
+ * @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public static function adjustSeatCount($license, $oldSeats, $newSeats)
@@ -164,21 +179,17 @@ class License extends Depreciable
// On Create, we just make one for each of the seats.
$change = abs($oldSeats - $newSeats);
if ($oldSeats > $newSeats) {
- $license->load('licenseseats.user');
// Need to delete seats... lets see if if we have enough.
- $seatsAvailableForDelete = $license->licenseseats->reject(function ($seat) {
- return ((bool) $seat->assigned_to) || ((bool) $seat->asset_id);
- });
+ $seatsAvailableForDelete = $license->licenseseats()->whereNull('assigned_to')->whereNull('asset_id')->limit($change);
if ($change > $seatsAvailableForDelete->count()) {
Session::flash('error', trans('admin/licenses/message.assoc_users'));
return false;
}
- for ($i = 1; $i <= $change; $i++) {
- $seatsAvailableForDelete->pop()->delete();
- }
+ $seatsAvailableForDelete->delete();
+
// Log Deletion of seats.
$logAction = new Actionlog;
$logAction->item_type = self::class;
@@ -203,11 +214,15 @@ class License extends Depreciable
}
//Chunk and use DB transactions to prevent timeouts.
- collect($licenseInsert)->chunk(1000)->each(function ($chunk) {
- DB::transaction(function () use ($chunk) {
- LicenseSeat::insert($chunk->toArray());
- });
- });
+ collect($licenseInsert)->chunk(1000)->each(
+ function ($chunk) {
+ DB::transaction(
+ function () use ($chunk) {
+ LicenseSeat::insert($chunk->toArray());
+ }
+ );
+ }
+ );
// On initial create, we shouldn't log the addition of seats.
if ($license->id) {
@@ -228,7 +243,7 @@ class License extends Depreciable
* Sets the attribute for whether or not the license is maintained
*
* @author A. Gianotto
- * @since [v1.0]
+ * @since [v1.0]
* @return mixed
*/
public function setMaintainedAttribute($value)
@@ -240,7 +255,7 @@ class License extends Depreciable
* Sets the reassignable attribute
*
* @author A. Gianotto
- * @since [v1.0]
+ * @since [v1.0]
* @return mixed
*/
public function setReassignableAttribute($value)
@@ -252,7 +267,7 @@ class License extends Depreciable
* Sets expiration date attribute
*
* @author A. Gianotto
- * @since [v1.0]
+ * @since [v1.0]
* @return mixed
*/
public function setExpirationDateAttribute($value)
@@ -269,7 +284,7 @@ class License extends Depreciable
* Sets termination date attribute
*
* @author A. Gianotto
- * @since [v2.0]
+ * @since [v2.0]
* @return mixed
*/
public function setTerminationDateAttribute($value)
@@ -285,10 +300,11 @@ class License extends Depreciable
* Sets free_seat_count attribute
*
* @author G. Martinez
- * @since [v6.3]
+ * @since [v6.3]
* @return mixed
*/
- public function getFreeSeatCountAttribute(){
+ public function getFreeSeatCountAttribute()
+ {
return $this->attributes['free_seat_count'] = $this->remaincount();
}
@@ -296,7 +312,7 @@ class License extends Depreciable
* Establishes the license -> company relationship
*
* @author A. Gianotto
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function company()
@@ -308,7 +324,7 @@ class License extends Depreciable
* Establishes the license -> category relationship
*
* @author A. Gianotto
- * @since [v4.4.0]
+ * @since [v4.4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function category()
@@ -320,7 +336,7 @@ class License extends Depreciable
* Establishes the license -> manufacturer relationship
*
* @author A. Gianotto
- * @since [v2.0]
+ * @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function manufacturer()
@@ -332,7 +348,7 @@ class License extends Depreciable
* Determine whether the user should be emailed on checkin/checkout
*
* @author A. Gianotto
- * @since [v2.0]
+ * @since [v2.0]
* @return bool
*/
public function checkin_email()
@@ -347,7 +363,7 @@ class License extends Depreciable
* Determine whether the user should be required to accept the license
*
* @author A. Gianotto