Merge branch 'refs/heads/develop' into bug/sc-26584

This commit is contained in:
spencerrlongg 2024-08-14 11:36:19 -05:00
commit a70b94e707
29 changed files with 365 additions and 92 deletions

View file

@ -218,6 +218,7 @@ class AcceptanceController extends Controller
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'eula' => $item->getEula(),
'note' => $request->input('note'),
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
@ -308,6 +309,7 @@ class AcceptanceController extends Controller
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'note' => $request->input('note'),
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,

View file

@ -227,7 +227,8 @@ class BulkAssetsController extends Controller
* its checkout status.
*/
if (($request->filled('purchase_date'))
if (($request->filled('name'))
|| ($request->filled('purchase_date'))
|| ($request->filled('expected_checkin'))
|| ($request->filled('purchase_cost'))
|| ($request->filled('supplier_id'))
@ -239,6 +240,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('status_id'))
|| ($request->filled('model_id'))
|| ($request->filled('next_audit_date'))
|| ($request->filled('null_name'))
|| ($request->filled('null_purchase_date'))
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
@ -251,13 +253,14 @@ class BulkAssetsController extends Controller
$this->update_array = [];
/**
* Leave out model_id and status here because we do math on that later. We have to do some extra
* validation and checks on those two.
* Leave out model_id and status here because we do math on that later. We have to do some
* extra validation and checks on those two.
*
* It's tempting to make these match the request check above, but some of these values require
* extra work to make sure the data makes sense.
*/
$this->conditionallyAddItem('purchase_date')
$this->conditionallyAddItem('name')
->conditionallyAddItem('purchase_date')
->conditionallyAddItem('expected_checkin')
->conditionallyAddItem('order_number')
->conditionallyAddItem('requestable')
@ -271,6 +274,11 @@ class BulkAssetsController extends Controller
/**
* Blank out fields that were requested to be blanked out via checkbox
*/
if ($request->input('null_name')=='1') {
$this->update_array['name'] = null;
}
if ($request->input('null_purchase_date')=='1') {
$this->update_array['purchase_date'] = null;
}

View file

@ -99,12 +99,18 @@ class SamlController extends Controller
{
$saml = $this->saml;
$auth = $saml->getAuth();
$auth->processResponse();
$saml_exception = false;
try {
$auth->processResponse();
} catch (\Exception $e) {
Log::warning("Exception caught in SAML login: " . $e->getMessage());
$saml_exception = true;
}
$errors = $auth->getErrors();
if (! empty($errors)) {
Log::error('There was an error with SAML ACS: '.implode(', ', $errors));
Log::error('Reason: '.$auth->getLastErrorReason());
if (!empty($errors) || $saml_exception) {
Log::warning('There was an error with SAML ACS: ' . implode(', ', $errors));
Log::warning('Reason: ' . $auth->getLastErrorReason());
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
}
@ -132,12 +138,18 @@ class SamlController extends Controller
{
$auth = $this->saml->getAuth();
$retrieveParametersFromServer = $this->saml->getSetting('retrieveParametersFromServer', false);
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
$saml_exception = false;
try {
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
} catch (\Exception $e) {
Log::warning("Exception caught in SAML single-logout: " . $e->getMessage());
$saml_exception = true;
}
$errors = $auth->getErrors();
if (! empty($errors)) {
Log::error('There was an error with SAML SLS: '.implode(', ', $errors));
Log::error('Reason: '.$auth->getLastErrorReason());
if (!empty($errors) || $saml_exception) {
Log::warning('There was an error with SAML SLS: ' . implode(', ', $errors));
Log::warning('Reason: ' . $auth->getLastErrorReason());
return view('errors.403');
}

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.
*

View file

@ -24,6 +24,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
$this->item_tag = $params['item_tag'];
$this->item_model = $params['item_model'];
$this->item_serial = $params['item_serial'];
$this->item_status = $params['item_status'];
$this->accepted_date = Helper::getFormattedDateObject($params['accepted_date'], 'date', false);
$this->assigned_to = $params['assigned_to'];
$this->note = $params['note'];
@ -65,6 +66,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
'item_tag' => $this->item_tag,
'item_model' => $this->item_model,
'item_serial' => $this->item_serial,
'item_status' => $this->item_status,
'note' => $this->note,
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,

View file

@ -24,6 +24,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
$this->item_tag = $params['item_tag'];
$this->item_model = $params['item_model'];
$this->item_serial = $params['item_serial'];
$this->item_status = $params['item_status'];
$this->declined_date = Helper::getFormattedDateObject($params['declined_date'], 'date', false);
$this->note = $params['note'];
$this->assigned_to = $params['assigned_to'];
@ -63,6 +64,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
'item_tag' => $this->item_tag,
'item_model' => $this->item_model,
'item_serial' => $this->item_serial,
'item_status' => $this->item_status,
'note' => $this->note,
'declined_date' => $this->declined_date,
'assigned_to' => $this->assigned_to,

View file

@ -162,6 +162,7 @@ class CheckinAssetNotification extends Notification
$message = (new MailMessage)->markdown('notifications.markdown.checkin-asset',
[
'item' => $this->item,
'status' => $this->item->assetstatus?->name,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,

View file

@ -209,6 +209,7 @@ public function toGoogleChat()
[
'item' => $this->item,
'admin' => $this->admin,
'status' => $this->item->assetstatus?->name,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,

View file

@ -337,12 +337,12 @@ class Saml
/**
* Get a setting.
*
* @author Johnson Yi <jyi.dev@outlook.com>
*
* @param string|array|int $key
* @param mixed $default
*
* @return void
* @return mixed
* @author Johnson Yi <jyi.dev@outlook.com>
*
*/
public function getSetting($key, $default = null)
{

View file

@ -78,7 +78,7 @@
"fakerphp/faker": "^1.16",
"larastan/larastan": "^2.9",
"mockery/mockery": "^1.4",
"nunomaduro/phpinsights": "^2.7",
"nunomaduro/phpinsights": "^2.11",
"php-mock/php-mock-phpunit": "^2.10",
"phpunit/phpunit": "^10.0",
"squizlabs/php_codesniffer": "^3.5",

2
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "35c741a2d3300848d758b187554b5b17",
"content-hash": "3819ab4ef72eb77fabe494c0e746b83b",
"packages": [
{
"name": "alek13/slack",

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=da3f7fee4a180ba924f6a3920c94eb71",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=f0b08873a06bb54daeee176a9459f4a9",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=f4397c717b99fce41a633ca6edd5d1f4",
"/css/build/overrides.css": "/css/build/overrides.css?id=e16bdd56ba78ee0f7247d88d8be191ff",
"/css/build/app.css": "/css/build/app.css?id=da3b545997adf9f04f2fd7dbff40a180",
"/css/build/overrides.css": "/css/build/overrides.css?id=a759aa24710e294392877c5139fda40e",
"/css/build/app.css": "/css/build/app.css?id=b3b3df70f679f45e15a6bcd28a8e87cc",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=393aaa7b368b0670fc42434c8cca7dc7",
@ -19,7 +19,7 @@
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=0640e45bad692dcf62873c6e85904899",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/all.css": "/css/dist/all.css?id=60b8cb7b38fe12383e530466a7e93705",
"/css/dist/all.css": "/css/dist/all.css?id=7b8e04041af3dfe3de25d73107bfda91",
"/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,136 @@ 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%;
}
}
.select2-selection__choice__remove {
color: white !important;
}
.select2-selection--multiple {
border-color: #d2d6de !important;
height: 34px;
}
.select2-selection__choice {
border-radius: 0px !important;
}
img.navbar-brand-img, .navbar-brand>img {
float: left;
padding: 5px 5px 5px 0;
@ -885,6 +1015,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

@ -30,7 +30,7 @@
<tr>
<td></td>
<td>{{ trans('admin/hardware/table.id') }}</td>
<td>{{ trans('admin/hardware/table.name') }}</td>
<td>{{ trans('general.asset_name') }}</td>
<td>{{ trans('admin/hardware/table.location')}}</td>
<td>{{ trans('admin/hardware/table.assigned_to') }}</td>
</tr>

View file

@ -35,6 +35,25 @@
@endif
</div>
<!-- Name -->
<div class="form-group {{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-3 control-label">
{{ trans('admin/hardware/form.name') }}
</label>
<div class="col-md-4">
<input type="text" class="form-control" name="name" id="name" value="{{ old('name') }}" maxlength="100" style="width:100%">
{!! $errors->first('name', '<span class="alert-msg" aria-hidden="true">
<i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
<div class="col-md-5">
<label class="form-control">
{{ Form::checkbox('null_name', '1', false) }}
{{ trans_choice('general.set_to_null', count($assets), ['asset_count' => count($assets)]) }}
</label>
</div>
</div>
<!-- Purchase Date -->
<div class="form-group {{ $errors->has('purchase_date') ? ' has-error' : '' }}">
<label for="purchase_date" class="col-md-3 control-label">{{ trans('admin/hardware/form.date') }}</label>

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

@ -144,7 +144,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
<ul class="nav navbar-nav">
@can('index', \App\Models\Asset::class)
<li aria-hidden="true"{!! (Request::is('hardware*') ? ' class="active"' : '') !!}>
<a href="{{ url('hardware') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=1" : ''}} tabindex="-1">
<a href="{{ url('hardware') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=1" : ''}} tabindex="-1" data-tooltip="true" data-placement="bottom" data-title="{{ trans('general.assets') }}">
<i class="fas fa-barcode fa-fw"></i>
<span class="sr-only">{{ trans('general.assets') }}</span>
</a>
@ -152,7 +152,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
@endcan
@can('view', \App\Models\License::class)
<li aria-hidden="true"{!! (Request::is('licenses*') ? ' class="active"' : '') !!}>
<a href="{{ route('licenses.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=2" : ''}} tabindex="-1">
<a href="{{ route('licenses.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=2" : ''}} tabindex="-1" data-tooltip="true" data-placement="bottom" data-title="{{ trans('general.licenses') }}">
<i class="far fa-save fa-fw"></i>
<span class="sr-only">{{ trans('general.licenses') }}</span>
</a>
@ -160,7 +160,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
@endcan
@can('index', \App\Models\Accessory::class)
<li aria-hidden="true"{!! (Request::is('accessories*') ? ' class="active"' : '') !!}>
<a href="{{ route('accessories.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=3" : ''}} tabindex="-1">
<a href="{{ route('accessories.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=3" : ''}} tabindex="-1" data-tooltip="true" data-placement="bottom" data-title="{{ trans('general.accessories') }}">
<i class="far fa-keyboard fa-fw"></i>
<span class="sr-only">{{ trans('general.accessories') }}</span>
</a>
@ -168,7 +168,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
@endcan
@can('index', \App\Models\Consumable::class)
<li aria-hidden="true"{!! (Request::is('consumables*') ? ' class="active"' : '') !!}>
<a href="{{ url('consumables') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=4" : ''}} tabindex="-1">
<a href="{{ url('consumables') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=4" : ''}} tabindex="-1" data-tooltip="true" data-placement="bottom" data-title="{{ trans('general.consumables') }}">
<i class="fas fa-tint fa-fw"></i>
<span class="sr-only">{{ trans('general.consumables') }}</span>
</a>
@ -176,7 +176,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
@endcan
@can('view', \App\Models\Component::class)
<li aria-hidden="true"{!! (Request::is('components*') ? ' class="active"' : '') !!}>
<a href="{{ route('components.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=5" : ''}} tabindex="-1">
<a href="{{ route('components.index') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=5" : ''}} tabindex="-1" data-tooltip="true" data-placement="bottom" data-title="{{ trans('general.components') }}">
<i class="far fa-hdd fa-fw"></i>
<span class="sr-only">{{ trans('general.components') }}</span>
</a>

View file

@ -16,6 +16,9 @@
@if (isset($note))
| **{{ trans('general.notes') }}** | {{ $note }} |
@endif
@if (isset($item_status))
| **{{ trans('general.status') }}** | {{ $item_status }} |
@endif
@if ((isset($item_tag)) && ($item_tag!=''))
| **{{ trans('mail.asset_tag') }}** | {{ $item_tag }} |
@endif

View file

@ -31,6 +31,9 @@
@if (isset($last_checkout))
| **{{ trans('mail.checkout_date') }}** | {{ $last_checkout }} |
@endif
@if (isset($status))
| **{{ trans('general.status') }}** | {{ $status }} |
@endif
@foreach($fields as $field)
@if (($item->{ $field->db_column_name() }!='') && ($field->show_in_email) && ($field->field_encrypted=='0'))
| **{{ $field->name }}** | {{ $item->{ $field->db_column_name() } }} |

View file

@ -31,6 +31,9 @@
@if (isset($last_checkout))
| **{{ trans('mail.checkout_date') }}** | {{ $last_checkout }} |
@endif
@if (isset($status))
| **{{ trans('general.status') }}** | {{ $status }} |
@endif
@if ((isset($expected_checkin)) && ($expected_checkin!=''))
| **{{ trans('mail.expecting_checkin_date') }}** | {{ $expected_checkin }} |
@endif

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

View file

@ -57,6 +57,7 @@ class BulkEditAssetsTest extends TestCase
$company1 = Company::factory()->create();
$company2 = Company::factory()->create();
$assets = Asset::factory()->count(10)->create([
'name' => 'Old Asset Name',
'purchase_date' => '2023-01-01',
'expected_checkin' => '2023-01-01',
'status_id' => $status1->id,
@ -77,6 +78,7 @@ class BulkEditAssetsTest extends TestCase
// submits the ids and new values for each attribute
$this->actingAs(User::factory()->editAssets()->create())->post(route('hardware/bulksave'), [
'ids' => $id_array,
'name' => 'New Asset Name',
'purchase_date' => '2024-01-01',
'expected_checkin' => '2024-01-01',
'status_id' => $status2->id,
@ -97,6 +99,7 @@ class BulkEditAssetsTest extends TestCase
$this->assertEquals('2024-01-01', $asset->purchase_date->format('Y-m-d'));
$this->assertEquals('2024-01-01', $asset->expected_checkin->format('Y-m-d'));
$this->assertEquals($status2->id, $asset->status_id);
$this->assertEquals('New Asset Name', $asset->name);
$this->assertEquals($model2->id, $asset->model_id);
$this->assertEquals(5678.92, $asset->purchase_cost);
$this->assertEquals($supplier2->id, $asset->supplier_id);
@ -109,6 +112,59 @@ class BulkEditAssetsTest extends TestCase
});
}
public function testBulkEditAssetsNullsOutFieldsIfSelected()
{
// sets up all needed models and attributes on the assets
// this test does not deal with custom fields - will be dealt with in separate cases
$status1 = Statuslabel::factory()->create();
$status2 = Statuslabel::factory()->create();
$model1 = AssetModel::factory()->create();
$model2 = AssetModel::factory()->create();
$supplier1 = Supplier::factory()->create();
$supplier2 = Supplier::factory()->create();
$company1 = Company::factory()->create();
$company2 = Company::factory()->create();
$assets = Asset::factory()->count(10)->create([
'name' => 'Old Asset Name',
'purchase_date' => '2023-01-01',
'expected_checkin' => '2023-01-01',
'status_id' => $status1->id,
'model_id' => $model1->id,
// skipping locations on this test, it deserves it's own test
'purchase_cost' => 1234.90,
'supplier_id' => $supplier1->id,
'company_id' => $company1->id,
'order_number' => '123456',
'warranty_months' => 24,
'next_audit_date' => '2024-06-01',
'requestable' => false
]);
// gets the ids together to submit to the endpoint
$id_array = $assets->pluck('id')->toArray();
// submits the ids and new values for each attribute
$this->actingAs(User::factory()->editAssets()->create())->post(route('hardware/bulksave'), [
'ids' => $id_array,
'null_name' => '1',
'null_purchase_date' => '1',
'null_expected_checkin_date' => '1',
'null_next_audit_date' => '1',
'status_id' => $status2->id,
'model_id' => $model2->id,
])
->assertStatus(302)
->assertSessionHasNoErrors();
// asserts that each asset has the updated values
Asset::findMany($id_array)->each(function (Asset $asset) use ($status2, $model2, $supplier2, $company2) {
$this->assertNull($asset->name);
$this->assertNull($asset->purchase_date);
$this->assertNull($asset->expected_checkin);
$this->assertNull($asset->next_audit_date);
});
}
public function testBulkEditAssetsAcceptsAndUpdatesUnencryptedCustomFields()
{
$this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL');

View file

@ -4,27 +4,35 @@ namespace Tests\Feature\Checkins\Ui;
use App\Models\Component;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ComponentCheckinTest extends TestCase
{
public function testCheckingInComponentRequiresCorrectPermission()
{
$component = Component::factory()->checkedOutToAsset()->create();
$componentAsset = DB::table('components_assets')->where('component_id', $component->id)->first();
$this->actingAs(User::factory()->create())
->post(route('components.checkin.store', [
'componentID' => Component::factory()->checkedOutToAsset()->create()->id,
'componentID' => $componentAsset->id,
]))
->assertForbidden();
}
public function testComponentCheckinPagePostIsRedirectedIfRedirectSelectionIsIndex()
{
$component = Component::factory()->checkedOutToAsset()->create();
$componentAsset = DB::table('components_assets')->where('component_id', $component->id)->first();
$this->actingAs(User::factory()->admin()->create())
->from(route('components.index'))
->post(route('components.checkin.store', $component), [
->post(route('components.checkin.store', [
'componentID' => $componentAsset->id,
]), [
'redirect_option' => 'index',
'checkin_qty' => 1,
])
@ -36,9 +44,13 @@ class ComponentCheckinTest extends TestCase
{
$component = Component::factory()->checkedOutToAsset()->create();
$componentAsset = DB::table('components_assets')->where('component_id', $component->id)->first();
$this->actingAs(User::factory()->admin()->create())
->from(route('components.index'))
->post(route('components.checkin.store', $component), [
->post(route('components.checkin.store', [
'componentID' => $componentAsset->id,
]), [
'redirect_option' => 'item',
'checkin_qty' => 1,
])
@ -46,6 +58,4 @@ class ComponentCheckinTest extends TestCase
->assertSessionHasNoErrors()
->assertRedirect(route('components.show', ['component' => $component->id]));
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Tests\Feature;
namespace Tests\Feature\Console;
use App\Models\Asset;
use App\Models\User;
@ -10,7 +10,6 @@ class FixupAssignedToAssignedTypeTest extends TestCase
{
public function testEmptyAssignedType()
{
$this->markTestIncomplete();
$asset = Asset::factory()->create();
$user = User::factory()->create();
$admin = User::factory()->admin()->create();
@ -18,15 +17,10 @@ class FixupAssignedToAssignedTypeTest extends TestCase
$asset->checkOut($user, $admin);
$asset->assigned_type=null; //blank out the assigned type
$asset->save();
print "Okay we set everything up~!!!\n";
$output = $this->artisan('snipeit:assigned-to-fixup --debug')->assertExitCode(0);
print "artisan ran!\n";
dump($output);
$asset = Asset::find($asset->id);
print "\n we refreshed the asset?";
dump($asset);
$this->assertEquals(User::class, $asset->assigned_type);
$this->artisan('snipeit:assigned-to-fixup --debug')->assertExitCode(0);
$this->assertEquals(User::class, $asset->fresh()->assigned_type);
}
public function testInvalidAssignedTo()
@ -37,21 +31,15 @@ class FixupAssignedToAssignedTypeTest extends TestCase
$admin = User::factory()->admin()->create();
$asset->checkOut($user, $admin);
// $asset->checkIn($user, $admin); //no such method btw
$asset->assigned_type=null;
$asset->assigned_to=null;
$asset->saveOrFail(); //*should* generate a 'checkin'?
$asset->assigned_to=$user->id; //incorrectly mark asset as partially checked-out
$asset->saveOrFail();
print "Okay we set everything up for test TWO~!!!\n";
$output = $this->artisan('snipeit:assigned-to-fixup --debug')->assertExitCode(0);
print "artisan ran TWO!\n";
dump($output);
$asset = Asset::find($asset->id);
print "\n we refreshed the asset?";
dump($asset);
$this->assertNull($asset->assigned_to);
$this->artisan('snipeit:assigned-to-fixup --debug')->assertExitCode(0);
$this->assertNull($asset->fresh()->assigned_to);
}
}