Merge remote-tracking branch 'origin/develop'

Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
This commit is contained in:
snipe 2024-08-13 15:47:00 +01:00
commit 198b76ebc2
10 changed files with 197 additions and 47 deletions

View file

@ -15,8 +15,7 @@ trait MayContainCustomFields
$asset_model = AssetModel::find($this->model_id);
}
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
// this is dependent on the asset update request PR
$asset_model = $this->asset;
$asset_model = $this->asset->model;
}
// collect the custom fields in the request
$validator->after(function ($validator) use ($asset_model) {
@ -25,7 +24,7 @@ trait MayContainCustomFields
});
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
if (count($request_fields) > 0) {
$request_fields->diff($asset_model->fieldset->fields->pluck('db_column'))
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
->each(function ($request_field_name) use ($request_fields, $validator) {
if (CustomField::where('db_column', $request_field_name)->exists()) {
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found_on_model'));

View file

@ -2,12 +2,14 @@
namespace App\Http\Requests;
use App\Http\Requests\Traits\MayContainCustomFields;
use App\Models\Asset;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class UpdateAssetRequest extends ImageUploadRequest
{
use MayContainCustomFields;
/**
* Determine if the user is authorized to make this request.
*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2,8 +2,8 @@
"/js/build/app.js": "/js/build/app.js?id=ae9230922bbd81534aabec227fd0cbe6",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=5f3abb12a286d6cb8aa523322d7f053b",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=89b7dcd91db033fb19ebbd7f7dcc0c35",
"/css/build/overrides.css": "/css/build/overrides.css?id=e58b01ee11e234d2d8a478add5ed9f7f",
"/css/build/app.css": "/css/build/app.css?id=fea8f707f64cce1c09514b214b43a1a9",
"/css/build/overrides.css": "/css/build/overrides.css?id=7f3e3085f59787de247a173b7a5dfc7e",
"/css/build/app.css": "/css/build/app.css?id=344c0bc1c10f585ddd5cc465f9f8a983",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=dd5eb6c76770bacaa2e960849d275516",
@ -19,7 +19,7 @@
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=34023bf46b7c2486b7468de9b750dbff",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/all.css": "/css/dist/all.css?id=f8d9f95b5584b6bda64f24b29076b273",
"/css/dist/all.css": "/css/dist/all.css?id=74d0443b048ab8af5358c4c829a497db",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",

View file

@ -363,8 +363,10 @@ body {
}
@media print {
a[href]:after {
content: none;
@page {
size: A4;
margin: 0mm;
}
.tab-content > .tab-pane {
@ -372,8 +374,123 @@ body {
opacity: 1 !important;
visibility: visible !important;
}
.img-responsive {
width: 200px;
}
html, body {
width: 1024px;
}
body {
margin: 0 auto;
line-height: 1em;
word-spacing:1px;
letter-spacing:0.2px;
font: 15px "Times New Roman", Times, serif;
background:white;
color:black;
width: 100%;
float: none;
}
/* avoid page-breaks inside a listingContainer*/
.listingContainer {
page-break-inside: avoid;
}
h1 {
font: 28px "Times New Roman", Times, serif;
}
h2 {
font: 24px "Times New Roman", Times, serif;
}
h3 {
font: 20px "Times New Roman", Times, serif;
}
/* Improve colour contrast of links */
a:link, a:visited {
color: #781351
}
/* URL */
a:link, a:visited {
background: transparent;
color:#333;
text-decoration:none;
}
a[href]:after {
content: "" !important;
}
a[href^="http://"] {
color:#000;
}
#header {
height:75px;
font-size: 24pt;
color:black
}
div.row-new-striped {
margin: 0px;
padding: 0px;
}
.pagination-detail, .fixed-table-toolbar {
visibility: hidden;
}
.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 .col-sm-pull-3 .col-sm-push-9 {
float: left;
}
.col-sm-12 {
width: 100%;
}
.col-sm-11 {
width: 91.66666666666666%;
}
.col-sm-10 {
width: 83.33333333333334%;
}
.col-sm-9 {
width: 75%;
}
.col-sm-8 {
width: 66.66666666666666%;
}
.col-sm-7 {
width: 58.333333333333336%;
}
.col-sm-6 {
width: 50%;
}
.col-sm-5 {
width: 41.66666666666667%;
}
.col-sm-4 {
width: 33.33333333333333%;
}
.col-sm-3 {
width: 25%;
}
.col-sm-2 {
width: 16.666666666666664%;
}
.col-sm-1 {
width: 8.333333333333332%;
}
}
img.navbar-brand-img, .navbar-brand>img {
float: left;
padding: 5px 5px 5px 0;
@ -885,6 +1002,10 @@ input[type="radio"]:checked::before {
padding-left:15px;
}
.nav-tabs-custom > .nav-tabs > li.active {
font-weight: bold;
}
/** --------------------------------------- **/
/** End checkbox styles to replace iCheck **/
/** --------------------------------------- **/

View file

@ -133,7 +133,7 @@
@can('update', \App\Models\Asset::class)
<li class="pull-right">
<li class="pull-right hidden-print">
<a href="#" data-toggle="modal" data-target="#uploadFileModal">
<i class="fas fa-paperclip" aria-hidden="true"></i>
{{ trans('button.upload') }}
@ -176,7 +176,7 @@
@if (($asset->assetstatus) && ($asset->assetstatus->deployable=='1'))
@if (($asset->assigned_to != '') && ($asset->deleted_at==''))
@can('checkin', \App\Models\Asset::class)
<div class="col-md-12">
<div class="col-md-12 hidden-print">
<span class="tooltip-wrapper"{!! (!$asset->model ? ' data-tooltip="true" title="'.trans('admin/hardware/general.model_invalid_fix').'"' : '') !!}>
<a role="button" href="{{ route('hardware.checkin.create', $asset->id) }}" class="btn btn-sm btn-primary btn-block hidden-print{{ (!$asset->model ? ' disabled' : '') }}">
{{ trans('admin/hardware/general.checkin') }}
@ -186,7 +186,7 @@
@endcan
@elseif (($asset->assigned_to == '') && ($asset->deleted_at==''))
@can('checkout', \App\Models\Asset::class)
<div class="col-md-12" style="padding-top: 5px;">
<div class="col-md-12 hidden-print" style="padding-top: 5px;">
<span class="tooltip-wrapper"{!! (!$asset->model ? ' data-tooltip="true" title="'.trans('admin/hardware/general.model_invalid_fix').'"' : '') !!}>
<a href="{{ route('hardware.checkout.create', $asset->id) }}" class="btn btn-sm btn-primary btn-block hidden-print{{ (!$asset->model ? ' disabled' : '') }}">
{{ trans('admin/hardware/general.checkout') }}
@ -199,7 +199,7 @@
@if ($asset->deleted_at=='')
@can('update', $asset)
<div class="col-md-12" style="padding-top: 5px;">
<div class="col-md-12 hidden-print" style="padding-top: 5px;">
<a href="{{ route('hardware.edit', $asset->id) }}" class="btn btn-sm btn-primary btn-block hidden-print">
{{ trans('admin/hardware/general.edit') }}
</a>
@ -207,7 +207,7 @@
@endcan
@can('audit', \App\Models\Asset::class)
<div class="col-md-12" style="padding-top: 5px;">
<div class="col-md-12 hidden-print" style="padding-top: 5px;">
<span class="tooltip-wrapper"{!! (!$asset->model ? ' data-tooltip="true" title="'.trans('admin/hardware/general.model_invalid_fix').'"' : '') !!}>
<a href="{{ route('asset.audit.create', $asset->id) }}" class="btn btn-sm btn-primary btn-block hidden-print{{ (!$asset->model ? ' disabled' : '') }}">
{{ trans('general.audit') }}
@ -218,7 +218,7 @@
@endif
@can('create', $asset)
<div class="col-md-12" style="padding-top: 5px;">
<div class="col-md-12 hidden-print" style="padding-top: 5px;">
<a href="{{ route('clone/hardware', $asset->id) }}" class="btn btn-sm btn-primary btn-block hidden-print">
{{ trans('admin/hardware/general.clone') }}
</a>
@ -226,7 +226,7 @@
@endcan
@can('delete', $asset)
<div class="col-md-12" style="padding-top: 30px; padding-bottom: 30px;">
<div class="col-md-12 hidden-print" style="padding-top: 30px; padding-bottom: 30px;">
@if ($asset->deleted_at=='')
<button class="btn btn-sm btn-block btn-danger delete-asset" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.sure_to_delete_var', ['item' => $asset->asset_tag]) }}" data-target="#dataConfirmModal">{{ trans('general.delete') }}
</button>
@ -247,7 +247,7 @@
</h2>
<p>
@if (($asset->checkedOutToUser()) && ($asset->assignedTo->present()->gravatar()))
<img src="{{ $asset->assignedTo->present()->gravatar() }}" class="user-image-inline" alt="{{ $asset->assignedTo->present()->fullName() }}">
<img src="{{ $asset->assignedTo->present()->gravatar() }}" class="user-image-inline hidden-print" alt="{{ $asset->assignedTo->present()->fullName() }}">
@endif
</p>
{!! $asset->assignedTo->present()->glyph() . ' ' .$asset->assignedTo->present()->nameUrl() !!}
@ -288,7 +288,7 @@
</li>
@endif
<li>
<i class="fas fa-calendar"></i> {{ trans('admin/hardware/form.checkout_date') }}: {{ Helper::getFormattedDateObject($asset->last_checkout, 'date', false) }}
<i class="fas fa-calendar hidden-print"></i> {{ trans('admin/hardware/form.checkout_date') }}: {{ Helper::getFormattedDateObject($asset->last_checkout, 'date', false) }}
</li>
@if (isset($asset->expected_checkin))
<li>
@ -324,7 +324,7 @@
<div class="col-md-9">
<span class="js-copy-assettag">{{ $asset->asset_tag }}</span>
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy-assettag" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<i class="fa-regular fa-clipboard js-copy-link hidden-print" data-clipboard-target=".js-copy-assettag" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<span class="sr-only">{{ trans('general.copy_to_clipboard') }}</span>
</i>
</div>
@ -378,6 +378,7 @@
</div>
@endif
@if ($asset->company)
<div class="row">
<div class="col-md-3">
@ -408,13 +409,26 @@
<div class="col-md-9">
<span class="js-copy-serial">{{ $asset->serial }}</span>
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy-serial" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<i class="fa-regular fa-clipboard js-copy-link hidden-print" data-clipboard-target=".js-copy-serial" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<span class="sr-only">{{ trans('general.copy_to_clipboard') }}</span>
</i>
</div>
</div>
@endif
@if ($asset->last_checkout!='')
<div class="row">
<div class="col-md-3">
<strong>
{{ trans('admin/hardware/table.checkout_date') }}
</strong>
</div>
<div class="col-md-9">
{{ Helper::getFormattedDateObject($asset->last_checkout, 'datetime', false) }}
</div>
</div>
@endif
@if ((isset($audit_log)) && ($audit_log->created_at))
<div class="row">
<div class="col-md-3">
@ -608,7 +622,7 @@
@endphp
@if ($fieldSize>0)
<span id="text-{{ $field->id }}-to-hide">{{ str_repeat('*', $fieldSize) }}</span>
<span class="js-copy-{{ $field->id }}" id="text-{{ $field->id }}-to-show" style="font-size: 0px;">
<span class="js-copy-{{ $field->id }} hidden-print" id="text-{{ $field->id }}-to-show" style="font-size: 0px;">
@if (($field->format=='URL') && ($asset->{$field->db_column_name()}!=''))
<a href="{{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }}" target="_new">{{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }}</a>
@elseif (($field->format=='DATE') && ($asset->{$field->db_column_name()}!=''))
@ -617,7 +631,7 @@
{{ Helper::gracefulDecrypt($field, $asset->{$field->db_column_name()}) }}
@endif
</span>
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy-{{ $field->id }}" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<i class="fa-regular fa-clipboard js-copy-link hidden-print" data-clipboard-target=".js-copy-{{ $field->id }}" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
<span class="sr-only">{{ trans('general.copy_to_clipboard') }}</span>
</i>
@endif
@ -950,18 +964,7 @@
</div>
</div>
@endif
@if ($asset->last_checkout!='')
<div class="row">
<div class="col-md-3">
<strong>
{{ trans('admin/hardware/table.checkout_date') }}
</strong>
</div>
<div class="col-md-9">
{{ Helper::getFormattedDateObject($asset->last_checkout, 'datetime', false) }}
</div>
</div>
@endif
@if ($asset->expected_checkin!='')
<div class="row">
<div class="col-md-3">
@ -1024,13 +1027,13 @@
{{ ($asset->userRequests) ? (int) $asset->userRequests->count() : '0' }}
</div>
</div>
<div class="row">
<div class="row hidden-print">
<div class="col-md-3">
<strong>
Labels
{{ trans('general.labels') }}
</strong>
</div>
<div class="col-md-9">
<div class="col-md-9 hidden-print">
{{ Form::open([
'method' => 'POST',
'route' => ['hardware/bulkedit'],
@ -1051,7 +1054,7 @@
</div><!-- /.tab-pane -->
<div class="tab-pane fade" id="software">
<div class="row">
<div class="row{{($asset->licenses->count() > 0 ) ? '' : ' hidden-print'}}">
<div class="col-md-12">
<!-- Licenses assets table -->
@if ($asset->licenses->count() > 0)
@ -1080,7 +1083,7 @@
{{ Helper::getFormattedDateObject($seat->license->expiration_date, 'date', false) }}
</td>
<td>
<a href="{{ route('licenses.checkin', $seat->id) }}" class="btn btn-sm bg-purple" data-tooltip="true">{{ trans('general.checkin') }}</a>
<a href="{{ route('licenses.checkin', $seat->id) }}" class="btn btn-sm bg-purple hidden-print" data-tooltip="true">{{ trans('general.checkin') }}</a>
</td>
</tr>
@endif
@ -1100,7 +1103,7 @@
<div class="tab-pane fade" id="components">
<!-- checked out assets table -->
<div class="row">
<div class="row{{($asset->components->count() > 0 ) ? '' : ' hidden-print'}}">
<div class="col-md-12">
@if($asset->components->count() > 0)
<table class="table table-striped">
@ -1155,7 +1158,7 @@
<div class="tab-pane fade" id="assets">
<div class="row">
<div class="row{{($asset->assignedAssets->count() > 0 ) ? '' : ' hidden-print'}}">
<div class="col-md-12">
@if ($asset->assignedAssets->count() > 0)
@ -1221,7 +1224,7 @@
<div class="tab-pane fade" id="maintenances">
<div class="row">
<div class="row{{($asset->assetmaintenances->count() > 0 ) ? '' : ' hidden-print'}}">
<div class="col-md-12">
@can('update', \App\Models\Asset::class)
<div id="maintenance-toolbar">
@ -1304,7 +1307,7 @@
</div> <!-- /.tab-pane history -->
<div class="tab-pane fade" id="files">
<div class="row">
<div class="row{{ ($asset->uploads->count() > 0 ) ? '' : ' hidden-print' }}">
<div class="col-md-12">
@if ($asset->uploads->count() > 0)
@ -1406,7 +1409,7 @@
@can('view', $asset->model)
<div class="tab-pane fade" id="modelfiles">
<div class="row">
<div class="row{{ (($asset->model) && ($asset->model->uploads->count() > 0)) ? '' : ' hidden-print' }}">
<div class="col-md-12">
@if (($asset->model) && ($asset->model->uploads->count() > 0))

View file

@ -1,3 +1,3 @@
<a style="padding-left: 10px; font-size: 18px;" class="text-dark-gray" data-trigger="focus" tabindex="0" role="button" data-toggle="popover" title="{{ trans('general.more_info') }}" data-placement="right" data-html="true" data-content="{{ (isset($helpText)) ? $helpText : 'Help Info Missing' }}">
<a style="padding-left: 10px; font-size: 18px;" class="text-dark-gray hidden-print" data-trigger="focus" tabindex="0" role="button" data-toggle="popover" title="{{ trans('general.more_info') }}" data-placement="right" data-html="true" data-content="{{ (isset($helpText)) ? $helpText : 'Help Info Missing' }}">
<i class="far fa-life-ring" aria-hidden="true"><span class="sr-only">{{ trans('general.moreinfo') }}</span></i>
</a>

View file

@ -454,4 +454,29 @@ class UpdateAssetTest extends TestCase
])
->assertStatusMessageIs('success');
}
public function testCustomFieldCannotBeUpdatedIfNotOnCurrentAssetModel()
{
$this->markIncompleteIfMySQL('Custom Field Tests do not work in MySQL');
$customField = CustomField::factory()->create();
$customField2 = CustomField::factory()->create();
$asset = Asset::factory()->hasMultipleCustomFields([$customField])->create();
$user = User::factory()->editAssets()->create();
// successful
$this->actingAsForApi($user)->patchJson(route('api.assets.update', $asset->id), [
$customField->db_column_name() => 'test attribute',
])->assertStatusMessageIs('success');
// custom field exists, but not on this asset model
$this->actingAsForApi($user)->patchJson(route('api.assets.update', $asset->id), [
$customField2->db_column_name() => 'test attribute',
])->assertStatusMessageIs('error');
// custom field does not exist
$this->actingAsForApi($user)->patchJson(route('api.assets.update', $asset->id), [
'_snipeit_non_existent_custom_field_50' => 'test attribute',
])->assertStatusMessageIs('error');
}
}