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 194 additions and 44 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.
*

Binary file not shown.

Binary file not shown.

Binary file not shown.

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');
}
}