mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-24 21:24:13 -08:00
Merge branch 'develop' into feature/sc-24120
This commit is contained in:
commit
430f1b0212
|
@ -1,4 +1,4 @@
|
|||
FROM alpine:3.17.3
|
||||
FROM alpine:3.18.5
|
||||
# Apache + PHP
|
||||
RUN apk add --no-cache \
|
||||
apache2 \
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-330-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
|
||||
![snipe-it-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
|
||||
|
||||
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-330-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
|
||||
|
||||
## Snipe-IT - Open Source Asset Management System
|
||||
|
||||
|
|
|
@ -84,35 +84,36 @@ class RestoreFromBackup extends Command
|
|||
|
||||
|
||||
$private_dirs = [
|
||||
'storage/private_uploads/accessories',
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
|
||||
'storage/private_uploads/audits',
|
||||
'storage/private_uploads/components',
|
||||
'storage/private_uploads/consumables',
|
||||
'storage/private_uploads/eula-pdfs',
|
||||
'storage/private_uploads/imports',
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/users',
|
||||
'storage/private_uploads/licenses',
|
||||
'storage/private_uploads/signatures',
|
||||
'storage/private_uploads/users',
|
||||
];
|
||||
$private_files = [
|
||||
'storage/oauth-private.key',
|
||||
'storage/oauth-public.key',
|
||||
];
|
||||
$public_dirs = [
|
||||
'public/uploads/accessories',
|
||||
'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',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/manufacturers',
|
||||
//'public/uploads/barcodes', // we don't want this, let the barcodes be regenerated
|
||||
'public/uploads/consumables',
|
||||
'public/uploads/departments',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/suppliers',
|
||||
'public/uploads/assets', // these are asset _pictures_, not asset files
|
||||
'public/uploads/locations',
|
||||
'public/uploads/accessories',
|
||||
'public/uploads/models',
|
||||
'public/uploads/categories',
|
||||
'public/uploads/avatars',
|
||||
'public/uploads/manufacturers',
|
||||
'public/uploads/models',
|
||||
'public/uploads/suppliers',
|
||||
];
|
||||
|
||||
$public_files = [
|
||||
|
|
|
@ -354,7 +354,7 @@ class Helper
|
|||
|
||||
if ($index >= $total_colors) {
|
||||
|
||||
\Log::error('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
\Log::info('Status label count is '.$index.' and exceeds the allowed count of 266.');
|
||||
//patch fix for array key overflow (color count starts at 1, array starts at 0)
|
||||
$index = $index - $total_colors - 1;
|
||||
|
||||
|
|
|
@ -116,41 +116,17 @@ class AssetMaintenancesController extends Controller
|
|||
{
|
||||
$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 = e($request->input('notes'));
|
||||
$asset = Asset::find(e($request->input('asset_id')));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot add a maintenance for that asset'));
|
||||
}
|
||||
|
||||
// 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->user_id = 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 = $completionDate->diffInDays($startDate);
|
||||
}
|
||||
$maintenance = new AssetMaintenance();
|
||||
$maintenance->fill($request->all());
|
||||
$maintenance->user_id = Auth::id();
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.create.success')));
|
||||
if ($maintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.create.success')));
|
||||
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $assetMaintenance->getErrors()));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -158,67 +134,41 @@ class AssetMaintenancesController extends Controller
|
|||
* Validates and stores an update to an asset maintenance
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @param int $assetMaintenanceId
|
||||
* @param int $id
|
||||
* @param int $request
|
||||
* @version v1.0
|
||||
* @since [v4.0]
|
||||
* @return string JSON
|
||||
*/
|
||||
public function update(Request $request, $assetMaintenanceId = null)
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', Asset::class);
|
||||
// Check if the asset maintenance exists
|
||||
$assetMaintenance = AssetMaintenance::findOrFail($assetMaintenanceId);
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
if ($maintenance = AssetMaintenance::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')])));
|
||||
}
|
||||
|
||||
$assetMaintenance->supplier_id = e($request->input('supplier_id'));
|
||||
$assetMaintenance->is_warranty = e($request->input('is_warranty'));
|
||||
$assetMaintenance->cost = $request->input('cost');
|
||||
$assetMaintenance->notes = e($request->input('notes'));
|
||||
|
||||
$asset = Asset::find(request('asset_id'));
|
||||
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot edit a maintenance for that asset'));
|
||||
// The asset this miantenance is attached to is not valid or has been deleted
|
||||
if (!$maintenance->asset) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.item_not_found', ['item_type' => trans('general.asset'), 'id' => $id])));
|
||||
}
|
||||
|
||||
// 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');
|
||||
$maintenance->fill($request->all());
|
||||
|
||||
if (($assetMaintenance->completion_date == null)
|
||||
) {
|
||||
if (($assetMaintenance->asset_maintenance_time !== 0)
|
||||
|| (! is_null($assetMaintenance->asset_maintenance_time))
|
||||
) {
|
||||
$assetMaintenance->asset_maintenance_time = null;
|
||||
}
|
||||
if ($maintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $maintenance, trans('admin/asset_maintenances/message.edit.success')));
|
||||
}
|
||||
|
||||
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 = $completionDate->diffInDays($startDate);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $maintenance->getErrors()));
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
if ($assetMaintenance->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $assetMaintenance, trans('admin/asset_maintenances/message.edit.success')));
|
||||
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, $assetMaintenance->getErrors()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an asset maintenance
|
||||
*
|
||||
|
|
|
@ -885,13 +885,17 @@ class AssetsController extends Controller
|
|||
public function checkin(Request $request, $asset_id)
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
$asset = Asset::with('model')->findOrFail($asset_id);
|
||||
$this->authorize('checkin', $asset);
|
||||
|
||||
|
||||
$target = $asset->assignedTo;
|
||||
if (is_null($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.already_checked_in')));
|
||||
}
|
||||
|
||||
$asset->expected_checkin = null;
|
||||
|
@ -925,7 +929,11 @@ class AssetsController extends Controller
|
|||
if ($asset->save()) {
|
||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
|
|
|
@ -353,6 +353,7 @@ class UsersController extends Controller
|
|||
|
||||
$user = new User;
|
||||
$user->fill($request->all());
|
||||
$user->created_by = Auth::user()->id;
|
||||
|
||||
if ($request->has('permissions')) {
|
||||
$permissions_array = $request->input('permissions');
|
||||
|
|
|
@ -88,7 +88,7 @@ class AssetModelsController extends Controller
|
|||
$model->requestable = Request::has('requestable');
|
||||
|
||||
if ($request->input('fieldset_id') != '') {
|
||||
$model->fieldset_id = e($request->input('fieldset_id'));
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
}
|
||||
|
||||
$model = $request->handleImages($model);
|
||||
|
@ -101,7 +101,6 @@ class AssetModelsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
// Redirect to the new model page
|
||||
return redirect()->route('models.index')->with('success', trans('admin/models/message.create.success'));
|
||||
}
|
||||
|
||||
|
@ -166,9 +165,6 @@ class AssetModelsController extends Controller
|
|||
|
||||
$this->removeCustomFieldsDefaultValues($model);
|
||||
|
||||
if ($request->input('fieldset_id') == '') {
|
||||
$model->fieldset_id = null;
|
||||
} else {
|
||||
$model->fieldset_id = $request->input('fieldset_id');
|
||||
|
||||
if ($this->shouldAddDefaultValues($request->input())) {
|
||||
|
@ -176,7 +172,7 @@ class AssetModelsController extends Controller
|
|||
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class AssetCheckoutController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $request->get('name'))) {
|
||||
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
|
||||
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ use App\Helpers\Helper;
|
|||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Setting;
|
||||
use App\View\Label;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -23,6 +25,13 @@ class BulkAssetsController extends Controller
|
|||
/**
|
||||
* Display the bulk edit page.
|
||||
*
|
||||
* This method is super weird because it's kinda of like a controller within a controller.
|
||||
* It's main function is to determine what the bulk action in, and then return a view with
|
||||
* the information that view needs, be it bulk delete, bulk edit, restore, etc.
|
||||
*
|
||||
* This is something that made sense at the time, but sort of doesn't make sense now. A JS front-end to determine form
|
||||
* action would make a lot more sense here and make things a lot more clear.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @return View
|
||||
* @internal param int $assetId
|
||||
|
@ -33,6 +42,9 @@ class BulkAssetsController extends Controller
|
|||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
/**
|
||||
* No asset IDs were passed
|
||||
*/
|
||||
if (! $request->filled('ids')) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
@ -41,40 +53,33 @@ class BulkAssetsController extends Controller
|
|||
$bulk_back_url = request()->headers->get('referer');
|
||||
session(['bulk_back_url' => $bulk_back_url]);
|
||||
|
||||
$asset_ids = array_values(array_unique($request->input('ids')));
|
||||
|
||||
//custom fields logic
|
||||
$asset_custom_field = Asset::with(['model.fieldset.fields', 'model'])->whereIn('id', $asset_ids)->whereHas('model', function ($query) {
|
||||
return $query->where('fieldset_id', '!=', null);
|
||||
})->get();
|
||||
$asset_ids = $request->input('ids');
|
||||
$assets = Asset::with('assignedTo', 'location', 'model')->find($asset_ids);
|
||||
|
||||
$models = $asset_custom_field->unique('model_id');
|
||||
$models = $assets->unique('model_id');
|
||||
$modelNames = [];
|
||||
foreach($models as $model) {
|
||||
$modelNames[] = $model->model->name;
|
||||
}
|
||||
|
||||
if ($request->filled('bulk_actions')) {
|
||||
|
||||
|
||||
switch ($request->input('bulk_actions')) {
|
||||
case 'labels':
|
||||
$this->authorize('view', Asset::class);
|
||||
$assets_found = Asset::find($asset_ids);
|
||||
|
||||
if ($assets_found->isEmpty()){
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
return (new Label)
|
||||
->with('assets', $assets_found)
|
||||
->with('assets', $assets)
|
||||
->with('settings', Setting::getSettings())
|
||||
->with('bulkedit', true)
|
||||
->with('count', 0);
|
||||
|
||||
case 'delete':
|
||||
$this->authorize('delete', Asset::class);
|
||||
$assets = Asset::with('assignedTo', 'location')->find($asset_ids);
|
||||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
$assets->each(function ($assets) {
|
||||
$this->authorize('delete', $assets);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-delete')->with('assets', $assets);
|
||||
|
@ -85,11 +90,11 @@ class BulkAssetsController extends Controller
|
|||
$assets->each(function ($asset) {
|
||||
$this->authorize('delete', $asset);
|
||||
});
|
||||
|
||||
return view('hardware/bulk-restore')->with('assets', $assets);
|
||||
|
||||
case 'edit':
|
||||
$this->authorize('update', Asset::class);
|
||||
|
||||
return view('hardware/bulk')
|
||||
->with('assets', $asset_ids)
|
||||
->with('statuslabel_list', Helper::statusLabelList())
|
||||
|
@ -117,25 +122,31 @@ class BulkAssetsController extends Controller
|
|||
|
||||
// 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');
|
||||
}
|
||||
|
||||
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
|
||||
|
||||
if (Session::exists('ids')) {
|
||||
$assets = Session::get('ids');
|
||||
} elseif (! $request->filled('ids') || count($request->input('ids')) <= 0) {
|
||||
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
}
|
||||
|
||||
$assets = array_keys($request->input('ids'));
|
||||
|
||||
if ($request->anyFilled($custom_field_columns)) {
|
||||
$custom_fields_present = true;
|
||||
} else {
|
||||
$custom_fields_present = false;
|
||||
}
|
||||
$assets = Asset::whereIn('id', array_keys($request->input('ids')))->get();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If ANY of these are filled, prepare to update the values on the assets.
|
||||
*
|
||||
* Additional checks will be needed for some of them to make sure the values
|
||||
* make sense (for example, changing the status ID to something incompatible with
|
||||
* its checkout status.
|
||||
*/
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
|| ($request->filled('expected_checkin'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
|
@ -154,15 +165,22 @@ class BulkAssetsController extends Controller
|
|||
|| ($request->anyFilled($custom_field_columns))
|
||||
|
||||
) {
|
||||
foreach ($assets as $assetId) {
|
||||
// Let's loop through those assets and build an update array
|
||||
foreach ($assets as $asset) {
|
||||
|
||||
$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.
|
||||
*
|
||||
* 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')
|
||||
->conditionallyAddItem('expected_checkin')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
->conditionallyAddItem('status_id')
|
||||
->conditionallyAddItem('supplier_id')
|
||||
->conditionallyAddItem('warranty_months')
|
||||
->conditionallyAddItem('next_audit_date');
|
||||
|
@ -170,6 +188,9 @@ class BulkAssetsController extends Controller
|
|||
$this->conditionallyAddItem($custom_field_column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blank out fields that were requested to be blanked out via checkbox
|
||||
*/
|
||||
if ($request->input('null_purchase_date')=='1') {
|
||||
$this->update_array['purchase_date'] = null;
|
||||
}
|
||||
|
@ -186,7 +207,6 @@ class BulkAssetsController extends Controller
|
|||
$this->update_array['purchase_cost'] = $request->input('purchase_cost');
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$this->update_array['company_id'] = $request->input('company_id');
|
||||
if ($request->input('company_id') == 'clear') {
|
||||
|
@ -194,48 +214,92 @@ class BulkAssetsController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the model ID - we need to do some extra checks here to make sure
|
||||
* the custom field values work for the custom fieldset rules around this asset. Uniqueness
|
||||
* and requiredness across the fieldset is particularly important, since those are
|
||||
* fieldset-specific attributes.
|
||||
*/
|
||||
if ($request->filled('model_id')) {
|
||||
$this->update_array['model_id'] = AssetModel::find($request->input('model_id'))->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* We're trying to change the status ID - we need to do some extra checks here to
|
||||
* make sure the status label type is one that makes sense for the state of the asset,
|
||||
* for example, we shouldn't be able to make an asset archived if it's currently assigned
|
||||
* to someone/something.
|
||||
*/
|
||||
if ($request->filled('status_id')) {
|
||||
$updated_status = Statuslabel::find($request->input('status_id'));
|
||||
|
||||
// We cannot assign a non-deployable status type if the asset is already assigned.
|
||||
// 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')
|
||||
) {
|
||||
$this->update_array['status_id'] = $updated_status->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We're changing the location ID - figure out which location we should apply
|
||||
* this change to:
|
||||
*
|
||||
* 0 - RTD location only
|
||||
* 1 - location ID and RTD location ID
|
||||
* 2 - location ID only
|
||||
*
|
||||
* Note: this is kinda dumb and we should just use human-readable values IMHO. - snipe
|
||||
*/
|
||||
if ($request->filled('rtd_location_id')) {
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '0')) {
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '1')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
$this->update_array['rtd_location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
if (($request->filled('update_real_loc')) && (($request->input('update_real_loc')) == '2')) {
|
||||
$this->update_array['location_id'] = $request->input('rtd_location_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------------
|
||||
* ANYTHING that happens past this foreach
|
||||
* WILL NOT BE logged in the edit log_meta data
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
$changed = [];
|
||||
$asset = Asset::find($assetId);
|
||||
|
||||
foreach ($this->update_array as $key => $value) {
|
||||
|
||||
if ($this->update_array[$key] != $asset->{$key}) {
|
||||
$changed[$key]['old'] = $asset->{$key};
|
||||
$changed[$key]['new'] = $this->update_array[$key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($custom_fields_present) {
|
||||
/**
|
||||
* Start all the custom fields shenanigans
|
||||
*/
|
||||
|
||||
$model = $asset->model()->first();
|
||||
// Does the model have a fieldset?
|
||||
if ($asset->model->fieldset) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
|
||||
// Use the rules of the new model fieldsets if the model changed
|
||||
if ($request->filled('model_id')) {
|
||||
$this->update_array['model_id'] = $request->input('model_id');
|
||||
$model = \App\Models\AssetModel::find($request->input('model_id'));
|
||||
}
|
||||
|
||||
|
||||
// Make sure this model is valid
|
||||
$assetCustomFields = ($model) ? $model->fieldset : null;
|
||||
|
||||
if ($assetCustomFields && $assetCustomFields->fields) {
|
||||
|
||||
foreach ($assetCustomFields->fields as $field) {
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted=='1')) {
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
|
||||
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
|
||||
|
||||
/*
|
||||
|
@ -260,7 +324,6 @@ class BulkAssetsController extends Controller
|
|||
*/
|
||||
} else {
|
||||
|
||||
|
||||
if ((array_key_exists($field->db_column, $this->update_array)) && ($asset->{$field->db_column} != $this->update_array[$field->db_column])) {
|
||||
|
||||
// Check if this is an array, and if so, flatten it
|
||||
|
@ -273,9 +336,7 @@ class BulkAssetsController extends Controller
|
|||
}
|
||||
|
||||
} // endforeach
|
||||
} // end custom field check
|
||||
} // end custom fields handler
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Check if it passes validation, and then try to save
|
||||
|
|
|
@ -266,7 +266,7 @@ class Asset extends Depreciable
|
|||
|
||||
/**
|
||||
* Determines if an asset is available for checkout.
|
||||
* This checks to see if the it's checked out to an invalid (deleted) user
|
||||
* This checks to see if it's checked out to an invalid (deleted) user
|
||||
* OR if the assigned_to and deleted_at fields on the asset are empty AND
|
||||
* that the status is deployable
|
||||
*
|
||||
|
@ -753,7 +753,7 @@ class Asset extends Depreciable
|
|||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> status relationship
|
||||
* Establishes the asset -> license seats relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
|
@ -921,6 +921,27 @@ class Asset extends Depreciable
|
|||
return $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* This sets the requestable to a boolean 0 or 1. This accounts for forms or API calls that
|
||||
* explicitly pass the requestable field but it has a null or empty value.
|
||||
*
|
||||
* This will also correctly parse a 1/0 if "true"/"false" is passed.
|
||||
*
|
||||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRequestableAttribute($value)
|
||||
{
|
||||
$this->attributes['requestable'] = (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
|
|
|
@ -20,10 +20,9 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
use SoftDeletes;
|
||||
use CompanyableChildTrait;
|
||||
use ValidatingTrait;
|
||||
protected $casts = [
|
||||
'start_date' => 'datetime',
|
||||
'completion_date' => 'datetime',
|
||||
];
|
||||
|
||||
|
||||
|
||||
protected $table = 'asset_maintenances';
|
||||
protected $rules = [
|
||||
'asset_id' => 'required|integer',
|
||||
|
@ -31,12 +30,31 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
'asset_maintenance_type' => 'required',
|
||||
'title' => 'required|max:100',
|
||||
'is_warranty' => 'boolean',
|
||||
'start_date' => 'required|date',
|
||||
'completion_date' => 'nullable|date',
|
||||
'start_date' => 'required|date_format:Y-m-d',
|
||||
'completion_date' => 'date_format:Y-m-d|nullable',
|
||||
'notes' => 'string|nullable',
|
||||
'cost' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'asset_id',
|
||||
'supplier_id',
|
||||
'asset_maintenance_type',
|
||||
'is_warranty',
|
||||
'start_date',
|
||||
'completion_date',
|
||||
'asset_maintenance_time',
|
||||
'notes',
|
||||
'cost',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
/**
|
||||
|
|
|
@ -74,12 +74,16 @@
|
|||
"unicodeveloper/laravel-password": "^1.0",
|
||||
"watson/validating": "^6.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ldap": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^6.6",
|
||||
"fakerphp/faker": "^1.16",
|
||||
"mockery/mockery": "^1.4",
|
||||
"nunomaduro/larastan": "^1.0",
|
||||
"nunomaduro/phpinsights": "^2.7",
|
||||
"php-mock/php-mock-phpunit": "^2.8",
|
||||
"phpunit/php-token-stream": "^3.1",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
|
|
224
composer.lock
generated
224
composer.lock
generated
|
@ -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": "348f96db24a0f8dfb595ee38b38b34eb",
|
||||
"content-hash": "f4f3b6b02d044ed3e54cdd509b01c3dc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
|
@ -7011,16 +7011,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.14",
|
||||
"version": "3.0.34",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "2f0b7af658cbea265cbb4a791d6c29a6613f98ef"
|
||||
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2f0b7af658cbea265cbb4a791d6c29a6613f98ef",
|
||||
"reference": "2f0b7af658cbea265cbb4a791d6c29a6613f98ef",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a",
|
||||
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -7032,6 +7032,7 @@
|
|||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
|
@ -7100,7 +7101,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.14"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.34"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -7116,7 +7117,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-04T05:15:45+00:00"
|
||||
"time": "2023-11-27T11:13:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
|
@ -14100,6 +14101,213 @@
|
|||
},
|
||||
"time": "2022-02-21T01:04:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-mock/php-mock",
|
||||
"version": "2.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-mock/php-mock.git",
|
||||
"reference": "6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-mock/php-mock/zipball/6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d",
|
||||
"reference": "6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0 || ^8.0",
|
||||
"phpunit/php-text-template": "^1 || ^2 || ^3"
|
||||
},
|
||||
"replace": {
|
||||
"malkusch/php-mock": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0 || ^9.0 || ^10.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"suggest": {
|
||||
"php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"autoload.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpmock\\": [
|
||||
"classes/",
|
||||
"tests/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"WTFPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Markus Malkusch",
|
||||
"email": "markus@malkusch.de",
|
||||
"homepage": "http://markus.malkusch.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.",
|
||||
"homepage": "https://github.com/php-mock/php-mock",
|
||||
"keywords": [
|
||||
"BDD",
|
||||
"TDD",
|
||||
"function",
|
||||
"mock",
|
||||
"stub",
|
||||
"test",
|
||||
"test double",
|
||||
"testing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-mock/php-mock/issues",
|
||||
"source": "https://github.com/php-mock/php-mock/tree/2.4.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/michalbundyra",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-12T20:48:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-mock/php-mock-integration",
|
||||
"version": "2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-mock/php-mock-integration.git",
|
||||
"reference": "04f4a8d5442ca457b102b5204673f77323e3edb5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/04f4a8d5442ca457b102b5204673f77323e3edb5",
|
||||
"reference": "04f4a8d5442ca457b102b5204673f77323e3edb5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"php-mock/php-mock": "^2.4",
|
||||
"phpunit/php-text-template": "^1 || ^2 || ^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7.27 || ^6 || ^7 || ^8 || ^9 || ^10"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpmock\\integration\\": "classes/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"WTFPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Markus Malkusch",
|
||||
"email": "markus@malkusch.de",
|
||||
"homepage": "http://markus.malkusch.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Integration package for PHP-Mock",
|
||||
"homepage": "https://github.com/php-mock/php-mock-integration",
|
||||
"keywords": [
|
||||
"BDD",
|
||||
"TDD",
|
||||
"function",
|
||||
"mock",
|
||||
"stub",
|
||||
"test",
|
||||
"test double"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-mock/php-mock-integration/issues",
|
||||
"source": "https://github.com/php-mock/php-mock-integration/tree/2.2.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/michalbundyra",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-13T09:51:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-mock/php-mock-phpunit",
|
||||
"version": "2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-mock/php-mock-phpunit.git",
|
||||
"reference": "56edee85ad3232caa0202f98f2a3c899ab16bdb7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/56edee85ad3232caa0202f98f2a3c899ab16bdb7",
|
||||
"reference": "56edee85ad3232caa0202f98f2a3c899ab16bdb7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7",
|
||||
"php-mock/php-mock-integration": "^2.2.1",
|
||||
"phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"autoload.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpmock\\phpunit\\": "classes/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"WTFPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Markus Malkusch",
|
||||
"email": "markus@malkusch.de",
|
||||
"homepage": "http://markus.malkusch.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.",
|
||||
"homepage": "https://github.com/php-mock/php-mock-phpunit",
|
||||
"keywords": [
|
||||
"BDD",
|
||||
"TDD",
|
||||
"function",
|
||||
"mock",
|
||||
"phpunit",
|
||||
"stub",
|
||||
"test",
|
||||
"test double",
|
||||
"testing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-mock/php-mock-phpunit/issues",
|
||||
"source": "https://github.com/php-mock/php-mock-phpunit/tree/2.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/michalbundyra",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-30T07:06:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-parallel-lint/php-parallel-lint",
|
||||
"version": "v1.3.2",
|
||||
|
@ -16600,5 +16808,5 @@
|
|||
"ext-pdo": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
@ -99,10 +99,22 @@ class CustomFieldFactory extends Factory
|
|||
return [
|
||||
'name' => 'Test Checkbox',
|
||||
'help_text' => 'This is a sample checkbox.',
|
||||
'field_values' => "One\nTwo\nThree",
|
||||
'field_values' => "One\r\nTwo\r\nThree",
|
||||
'element' => 'checkbox',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function testRadio()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'name' => 'Test Radio',
|
||||
'help_text' => 'This is a sample radio.',
|
||||
'field_values' => "One\r\nTwo\r\nThree",
|
||||
'element' => 'radio',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class CustomFieldSeeder extends Seeder
|
|||
CustomField::factory()->count(1)->macAddress()->create();
|
||||
CustomField::factory()->count(1)->testEncrypted()->create();
|
||||
CustomField::factory()->count(1)->testCheckbox()->create();
|
||||
CustomField::factory()->count(1)->testRadio()->create();
|
||||
|
||||
|
||||
DB::table('custom_field_custom_fieldset')->insert([
|
||||
|
@ -96,6 +97,19 @@ class CustomFieldSeeder extends Seeder
|
|||
'required' => 0,
|
||||
],
|
||||
|
||||
[
|
||||
'custom_field_id' => '8',
|
||||
'custom_fieldset_id' => '2',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
[
|
||||
'custom_field_id' => '8',
|
||||
'custom_fieldset_id' => '1',
|
||||
'order' => 0,
|
||||
'required' => 0,
|
||||
],
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
"ajv": "^6.12.6",
|
||||
"alpinejs": "^3.13.2",
|
||||
"alpinejs": "^3.13.3",
|
||||
"blueimp-file-upload": "^9.34.0",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.css
vendored
BIN
public/css/dist/skins/skin-black-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
Binary file not shown.
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=41293fc7aa00ece89fd524e1e0e31a68",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=1681749b20329c40a052156ea4cb42fe",
|
||||
"/css/build/app.css": "/css/build/app.css?id=cc0fd2d77504fdd7f03e91e2369d02a9",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=8453824ff928a55d685947c84e6b3079",
|
||||
"/css/build/app.css": "/css/build/app.css?id=ddf74e5777fbad72decf760fe8e57570",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=dc383f8560a8d4adb51d44fb4043e03b",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=e6e53eef152bba01a4c666a4d8b01117",
|
||||
|
@ -12,13 +12,13 @@
|
|||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=7d92dea45d94be7e1d4e427c728d335d",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=8ca888bbc050d9680cbb65021382acba",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=bdfc704731682c67645a2248b0b8d2d7",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=b061bb141af3bdb6280c6ee772cf8f4f",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=d419cb63a12dc175d71645c876bfc2ab",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=391b67f142742a6b11a92b042c6d475c",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=5fa237d9baa78a00f78c75819cd794e6",
|
||||
"/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",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=a656b2d865fe379d8851757e8e4001ef",
|
||||
|
@ -37,7 +37,7 @@
|
|||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=d419cb63a12dc175d71645c876bfc2ab",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=bdfc704731682c67645a2248b0b8d2d7",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=b061bb141af3bdb6280c6ee772cf8f4f",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=07273f6ca3c698a39e8fc2075af4fa07",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
|
|
|
@ -399,7 +399,7 @@ img.navbar-brand-img, .navbar-brand>img {
|
|||
}
|
||||
|
||||
.icon-med {
|
||||
font-size: 20px;
|
||||
font-size: 14px;
|
||||
color: #889195;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,11 +124,11 @@ a {
|
|||
--button-primary: darken(@black, 25%);
|
||||
--button-hover: darken(@black, 30%);
|
||||
--header: @black; /* Use same as Header picker */
|
||||
--text-main: #BBB;
|
||||
--text-main: #fff;
|
||||
--text-sub: #9b9b9b;
|
||||
--link: #AAA; /* Use same as Header picker, lighten by 70% */
|
||||
--visited-link: lighten(@black, 40%); /* Use same as Header picker, lighten by 70% */
|
||||
--hover-link: lighten(@black, 45%); /* Use same as Header picker, lighten by 70% */
|
||||
--link: #fff; /* Use same as Header picker, lighten by 70% */
|
||||
--visited-link: #fff; /* Use same as Header picker, lighten by 70% */
|
||||
--hover-link: #949494; /* Use same as Header picker, lighten by 70% */
|
||||
--nav-link: #FFF; /* Use same as Header picker */
|
||||
--light-link: #fff; /* Use same as Header picker */
|
||||
}
|
||||
|
@ -193,18 +193,6 @@ h2.task_menu{
|
|||
color: var(--text-main);
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--nav-link);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--hover-link);
|
||||
}
|
||||
|
||||
.btn-primary.hover {
|
||||
color: var(--nav-link);
|
||||
}
|
||||
|
@ -318,8 +306,11 @@ input[type=text], input[type=search] {
|
|||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
.search-highlight, .search-highlight:hover{
|
||||
background-color: var(--back-sub) !important;
|
||||
}
|
||||
.input-group, .input-group-addon {
|
||||
background-color: var(--back-sub)!important;
|
||||
background-color: var(--back-sub);
|
||||
color: var(--text-main);
|
||||
}
|
||||
#licensesTable>tbody>tr>td>nobr>a>i.fa {
|
||||
|
@ -363,11 +354,11 @@ input[type=text], input[type=search] {
|
|||
}
|
||||
.select2-container--default .select2-results__option[aria-selected=true], .select2-container--default .select2-results__option[aria-selected=true]:hover {
|
||||
background-color: var(--back-sub);
|
||||
color: var(--header);
|
||||
color: var(--nav-link);
|
||||
}
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: var(--header);
|
||||
color: var(--back-main);
|
||||
background-color: var(--back-sub);
|
||||
color: var(--visited-link);
|
||||
}
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
color: var(--text-main);
|
||||
|
@ -426,9 +417,6 @@ a {
|
|||
color: var(--hover-link);
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:visited {
|
||||
color: var(--visited-link)
|
||||
}
|
||||
}
|
||||
|
||||
.row-striped {
|
||||
|
@ -464,3 +452,6 @@ a {
|
|||
div.container.row-new-striped{
|
||||
background-color: var(--back-sub);
|
||||
}
|
||||
.table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th {
|
||||
background-color: var(--back-sub);
|
||||
}
|
|
@ -9,6 +9,7 @@ return [
|
|||
'ad_append_domain_help' => 'User isn\'t required to write "username@domain.local", they can just type "username".',
|
||||
'admin_cc_email' => 'CC Email',
|
||||
'admin_cc_email_help' => 'If you would like to send a copy of checkin/checkout emails that are sent to users to an additional email account, enter it here. Otherwise leave this field blank.',
|
||||
'admin_settings' => 'Admin Settings',
|
||||
'is_ad' => 'This is an Active Directory server',
|
||||
'alerts' => 'Alerts',
|
||||
'alert_title' => 'Update Notification Settings',
|
||||
|
|
|
@ -21,6 +21,7 @@ return array(
|
|||
'manager' => 'Manager',
|
||||
'managed_locations' => 'Managed Locations',
|
||||
'name' => 'Name',
|
||||
'nogroup' => 'No groups have been created yet. To add one, visit: ',
|
||||
'notes' => 'Notes',
|
||||
'password_confirm' => 'Confirm Password',
|
||||
'password' => 'Password',
|
||||
|
|
|
@ -156,6 +156,7 @@ return [
|
|||
'image_filetypes_help' => 'Accepted filetypes are jpg, webp, png, gif, and svg. Max upload size allowed is :size.',
|
||||
'unaccepted_image_type' => 'This image file was not readable. Accepted filetypes are jpg, webp, png, gif, and svg. The mimetype of this file is: :mimetype.',
|
||||
'import' => 'Import',
|
||||
'import_this_file' => 'Map fields and process this file',
|
||||
'importing' => 'Importing',
|
||||
'importing_help' => 'You can import assets, accessories, licenses, components, consumables, and users via CSV file. <br><br>The CSV should be comma-delimited and formatted with headers that match the ones in the <a href="https://snipe-it.readme.io/docs/importing" target="_new">sample CSVs in the documentation</a>.',
|
||||
'import-history' => 'Import History',
|
||||
|
@ -491,5 +492,11 @@ return [
|
|||
'upload_error' => 'Error uploading file. Please check that there are no empty rows and that no column names are duplicated.',
|
||||
'copy_to_clipboard' => 'Copy to Clipboard',
|
||||
'copied' => 'Copied!',
|
||||
'status_compatibility' => 'If assets are already assigned, they cannot be changed to a non-deployable status type and this value change will be skipped.',
|
||||
'rtd_location_help' => 'This is the location of the asset when it is not checked out',
|
||||
'item_not_found' => ':item_type ID :id does not exist or has been deleted',
|
||||
'action_permission_denied' => 'You do not have permission to :action :item_type ID :id',
|
||||
'action_permission_generic' => 'You do not have permission to :action this :item_type',
|
||||
'edit' => 'edit',
|
||||
|
||||
];
|
||||
|
|
|
@ -491,7 +491,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-4">{{ trans('general.name') }}</th>
|
||||
<th class="col-md-4">{{ trans('admin/hardware/form.serial') }}</th>
|
||||
<th class="col-md-4">{{ trans('admin/licenses/form.license_key') }}</th>
|
||||
<th class="col-md-4">{{ trans('general.category') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<tr>
|
||||
<td></td>
|
||||
<td>{{ trans('admin/hardware/table.id') }}</td>
|
||||
<td>{{ trans('admin/hardware/table.name') }}</td>
|
||||
<td>{{ trans('admin/hardware/form.name') }}</td>
|
||||
<td>{{ trans('admin/hardware/table.location')}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -19,18 +19,22 @@
|
|||
|
||||
<p>{{ trans('admin/hardware/form.bulk_update_help') }}</p>
|
||||
|
||||
<div class="callout callout-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ trans_choice('admin/hardware/form.bulk_update_warn', count($assets), ['asset_count' => count($assets)]) }}
|
||||
@if (count($models) > 0)
|
||||
{{ trans_choice('admin/hardware/form.bulk_update_with_custom_field', count($models), ['asset_model_count' => count($models)]) }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
<form class="form-horizontal" method="post" action="{{ route('hardware/bulksave') }}" autocomplete="off" role="form">
|
||||
{{ csrf_field() }}
|
||||
|
||||
<div class="box box-default">
|
||||
<div class="box-body">
|
||||
|
||||
<div class="callout callout-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ trans_choice('admin/hardware/form.bulk_update_warn', count($assets), ['asset_count' => count($assets)]) }}
|
||||
|
||||
@if (count($models) > 0)
|
||||
{{ trans_choice('admin/hardware/form.bulk_update_with_custom_field', count($models), ['asset_model_count' => count($models)]) }}
|
||||
@endif
|
||||
</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>
|
||||
|
@ -76,6 +80,7 @@
|
|||
</label>
|
||||
<div class="col-md-7">
|
||||
{{ Form::select('status_id', $statuslabel_list , old('status_id'), array('class'=>'select2', 'style'=>'width:100%', 'aria-label'=>'status_id')) }}
|
||||
<p class="help-block">{{ trans('general.status_compatibility') }}</p>
|
||||
{!! $errors->first('status_id', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
@endif
|
||||
|
||||
@include ('partials.forms.edit.notes')
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('admin/hardware/form.default_location'), 'fieldname' => 'rtd_location_id'])
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('admin/hardware/form.default_location'), 'fieldname' => 'rtd_location_id', 'help_text' => trans('general.rtd_location_help')])
|
||||
@include ('partials.forms.edit.requestable', ['requestable_text' => trans('admin/hardware/general.requestable')])
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>{{ trans('general.asset_tag') }}</th>
|
||||
<th>{{ trans('general.asset_model') }}</th>
|
||||
<th>{{ trans('general.model_no') }}</th>
|
||||
<th>{{ trans('general.quickscan_checkin_status') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -126,7 +128,7 @@
|
|||
data : formData,
|
||||
success : function (data) {
|
||||
if (data.status == 'success') {
|
||||
$('#checkedin tbody').prepend("<tr class='success'><td>" + data.payload.asset + "</td><td>" + data.messages + "</td><td><i class='fas fa-check text-success'></i></td></tr>");
|
||||
$('#checkedin tbody').prepend("<tr class='success'><td>" + data.payload.asset_tag + "</td><td>" + data.payload.model + "</td><td>" + data.payload.model_number + "</td><td>" + data.messages + "</td><td><i class='fas fa-check text-success'></i></td></tr>");
|
||||
incrementOnSuccess();
|
||||
} else {
|
||||
handlecheckinFail(data);
|
||||
|
@ -146,17 +148,21 @@
|
|||
});
|
||||
|
||||
function handlecheckinFail (data) {
|
||||
if (data.payload.asset) {
|
||||
var asset = data.payload.asset;
|
||||
if (data.payload.asset_tag) {
|
||||
var asset_tag = data.payload.asset_tag;
|
||||
var model = data.payload.model;
|
||||
var model_number = data.payload.model_number;
|
||||
} else {
|
||||
var asset = '';
|
||||
var asset_tag = '';
|
||||
var model = '';
|
||||
var model_number = '';
|
||||
}
|
||||
if (data.messages) {
|
||||
var messages = data.messages;
|
||||
} else {
|
||||
var messages = '';
|
||||
}
|
||||
$('#checkedin tbody').prepend("<tr class='danger'><td>" + asset + "</td><td>" + messages + "</td><td><i class='fas fa-times text-danger'></i></td></tr>");
|
||||
$('#checkedin tbody').prepend("<tr class='danger'><td>" + asset_tag + "</td><td>" + model + "</td><td>" + model_number + "</td><td>" + messages + "</td><td><i class='fas fa-times text-danger'></i></td></tr>");
|
||||
}
|
||||
|
||||
function incrementOnSuccess() {
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
|
||||
<th class="col-md-3" data-sortable="true">{{ trans('admin/hardware/table.requesting_user') }}</th>
|
||||
<th class="col-md-2">{{ trans('admin/hardware/table.requested_date') }}</th>
|
||||
<th class="col-md-1">{{ trans('button.actions') }}</th> <th></th>
|
||||
<th class="col-md-1">{{ trans('button.actions') }}</th>
|
||||
<th class="col-md-1">{{ trans('general.checkout') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -1206,15 +1206,15 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="action_date" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th class="col-sm-1" data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th class="col-sm-1" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
<th class="col-sm-2" data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th class="col-md-3" data-field="signature_file" data-visible="false" data-formatter="imageFormatter">{{ trans('general.signature') }}</th>
|
||||
<th class="col-md-3" data-visible="false" data-field="file" data-visible="false" data-formatter="fileUploadFormatter">{{ trans('general.download') }}</th>
|
||||
<th class="col-sm-2" data-field="log_meta" data-visible="true" data-formatter="changeLogFormatter">{{ trans('admin/hardware/table.changed')}}</th>
|
||||
<th data-visible="true" data-field="action_date" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
<th data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th data-field="signature_file" data-visible="false" data-formatter="imageFormatter">{{ trans('general.signature') }}</th>
|
||||
<th data-visible="false" data-field="file" data-visible="false" data-formatter="fileUploadFormatter">{{ trans('general.download') }}</th>
|
||||
<th data-field="log_meta" data-visible="true" data-formatter="changeLogFormatter">{{ trans('admin/hardware/table.changed')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
|
|
@ -624,7 +624,7 @@
|
|||
@can('import')
|
||||
<li{!! (Request::is('import/*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('imports.index') }}">
|
||||
<i class="fas fa-cloud-download-alt fa-fw" aria-hidden="true"></i>
|
||||
<i class="fas fa-cloud-upload-alt fa-fw" aria-hidden="true"></i>
|
||||
<span>{{ trans('general.import') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<!-- Serial -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{ trans('admin/hardware/form.serial') }}</label>
|
||||
<label class="col-sm-2 control-label">{{ trans('admin/licenses/form.license_key') }}</label>
|
||||
<div class="col-md-6">
|
||||
<p class="form-control-static">
|
||||
@can('viewKeys', $licenseSeat->license)
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
<!-- Serial -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{{ trans('admin/hardware/form.serial') }}</label>
|
||||
<label class="col-sm-3 control-label">{{ trans('admin/licenses/form.license_key') }}</label>
|
||||
<div class="col-md-9">
|
||||
<p class="form-control-static" style="word-wrap: break-word;">
|
||||
@can('viewKeys', $license)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<span> {{-- This <span> doesn't seem to fix it, neither does a div? --}}
|
||||
<span>
|
||||
|
||||
<div class="form-group{{ $errors->has('custom_fieldset') ? ' has-error' : '' }}">
|
||||
<label for="custom_fieldset" class="col-md-3 control-label">{{ trans('admin/models/general.fieldset') }}</label>
|
||||
<label for="custom_fieldset" class="col-md-3 control-label">
|
||||
{{ trans('admin/models/general.fieldset') }}
|
||||
</label>
|
||||
<div class="col-md-5">
|
||||
{{ Form::select('fieldset_id', Helper::customFieldsetList(), old('fieldset_id', $fieldset_id), array('class'=>'select2 js-fieldset-field livewire-select2', 'style'=>'width:100%; min-width:350px', 'aria-label'=>'custom_fieldset', 'data-livewire-component' => $_instance->id)) }}
|
||||
{!! $errors->first('custom_fieldset', '<span class="alert-msg" aria-hidden="true"><br><i class="fas fa-times"></i> :message</span>') !!}
|
||||
|
@ -12,43 +15,66 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($this->add_default_values ) {{-- 'if the checkbox is enabled *AND* there are more than 0 fields in the fieldsset' --}}
|
||||
<div style="padding-left: 10px; padding-bottom: 0px; margin-bottom: -15px;">
|
||||
<div class="form-group">
|
||||
@if ($fields)
|
||||
|
||||
@foreach ($fields as $field)
|
||||
<div class="form-group">
|
||||
|
||||
<label class="col-md-3 control-label{{ $errors->has($field->name) ? ' has-error' : '' }}" for="default-value{{ $field->id }}">{{ $field->name }}</label>
|
||||
<label class="col-md-3 control-label{{ $errors->has($field->name) ? ' has-error' : '' }}">{{ $field->name }}</label>
|
||||
|
||||
<div class="col-md-7">
|
||||
|
||||
@if ($field->format == "DATE")
|
||||
|
||||
<div class="input-group col-md-4" style="padding-left: 0px;">
|
||||
<div class="input-group date" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-autoclose="true">
|
||||
<input type="text" class="form-control" placeholder="{{ trans('general.select_date') }}" name="default_values[{{ $field->id }}]" id="default-value{{ $field->id }}" value="{{ $field->defaultValue($model_id) }}">
|
||||
<span class="input-group-addon"><i class="fas fa-calendar" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@elseif ($field->element == "text")
|
||||
<input class="form-control m-b-xs" type="text" value="{{ $field->defaultValue($model_id) }}" id="default-value{{ $field->id }}" name="default_values[{{ $field->id }}]">
|
||||
|
||||
|
||||
<input class="form-control" type="text" value="{{ $field->defaultValue($model_id) }}" id="default-value{{ $field->id }}" name="default_values[{{ $field->id }}]">
|
||||
|
||||
|
||||
@elseif($field->element == "textarea")
|
||||
<textarea class="form-control" style="width: 100%;" id="default-value{{ $field->id }}" name="default_values[{{ $field->id }}]">{{ $field->defaultValue($model_id) }}</textarea><br>
|
||||
|
||||
|
||||
<textarea class="form-control" style="width: 100%;" id="default-value{{ $field->id }}" name="default_values[{{ $field->id }}]">{{ $field->defaultValue($model_id) }}</textarea>
|
||||
|
||||
|
||||
@elseif($field->element == "listbox")
|
||||
|
||||
<select class="form-control m-b-xs" name="default_values[{{ $field->id }}]">
|
||||
|
||||
<select class="form-control" name="default_values[{{ $field->id }}]">
|
||||
<option value=""></option>
|
||||
@foreach(explode("\r\n", $field->field_values) as $field_value)
|
||||
<option value="{{$field_value}}" {{ $field->defaultValue($model_id) == $field_value ? 'selected="selected"': '' }}>{{ $field_value }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
|
||||
@elseif($field->element == "radio")
|
||||
|
||||
@foreach(explode("\r\n", $field->field_values) as $field_value)
|
||||
<input type='radio' name="default_values[{{ $field->id }}]" value="{{$field_value}}" {{ $field->defaultValue($model_id) == $field_value ? 'checked="checked"': '' }} />{{ $field_value }}<br />
|
||||
<label class="col-md-3 form-control" for="{{ str_slug($field_value) }}">
|
||||
<input id="{{ str_slug($field_value) }}" aria-label="{{ str_slug($field->name) }}" type='radio' name="default_values[{{ $field->id }}]" value="{{$field_value}}" {{ $field->defaultValue($model_id) == $field_value ? 'checked="checked"': '' }} />{{ $field_value }}
|
||||
</label>
|
||||
@endforeach
|
||||
|
||||
@elseif($field->element == "checkbox")
|
||||
|
||||
@foreach(explode("\r\n", $field->field_values) as $field_value)
|
||||
<input type='checkbox' name="default_values[{{ $field->id }}][]" value="{{$field_value}}" {{ in_array($field_value, explode(', ',$field->defaultValue($model_id))) ? 'checked="checked"': '' }} /> {{ $field_value }}<br />
|
||||
<label class="col-md-3 form-control" for="{{ str_slug($field_value) }}">
|
||||
<input id="{{ str_slug($field_value) }}" type="checkbox" aria-label="{{ str_slug($field->name) }}" name="default_values[{{ $field->id }}][]" value="{{ $field_value }}"{{ in_array($field_value, explode(', ',$field->defaultValue($model_id))) ? ' checked="checked"': '' }}> {{ $field_value }}
|
||||
</label>
|
||||
@endforeach
|
||||
|
||||
|
||||
@else
|
||||
<span class="help-block form-error">
|
||||
Unknown field element: {{ $field->element }}
|
||||
|
@ -58,9 +84,8 @@
|
|||
</div>
|
||||
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</span>
|
||||
|
|
|
@ -129,8 +129,8 @@
|
|||
<td class="col-md-3">{{ Helper::getFormattedDateObject($currentFile->created_at, 'datetime', false) }}</td>
|
||||
<td class="col-md-1">{{ Helper::formatFilesizeUnits($currentFile->filesize) }}</td>
|
||||
<td class="col-md-1 text-right" style="white-space: nowrap;">
|
||||
<button class="btn btn-sm btn-info" wire:click="selectFile({{ $currentFile->id }})">
|
||||
<i class="fas fa-retweet fa-fw" aria-hidden="true"></i>
|
||||
<button class="btn btn-sm btn-info" wire:click="selectFile({{ $currentFile->id }})" data-tooltip="true" title="{{ trans('general.import_this_file') }}">
|
||||
<i class="fa-solid fa-list-check" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.import') }}</span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" wire:click="destroy({{ $currentFile->id }})">
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
data-id-table="system-backups"
|
||||
data-search="true"
|
||||
data-side-pagination="client"
|
||||
data-sort-order="asc"
|
||||
data-sort-order="desc"
|
||||
data-sort-name="modified_display"
|
||||
id="system-backups"
|
||||
class="table table-striped snipe-table">
|
||||
<thead>
|
||||
|
|
|
@ -553,7 +553,7 @@
|
|||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p>No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.</p>
|
||||
<p>{{ trans('admin/users/table.nogroup') }} <code>{{ trans('admin/settings/general.admin_settings') }} <i class="fa fa-cogs"></i> > {{ trans('general.groups') }} <i class="fas fa-user-friends"></i></code> </p>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
|
|
@ -638,13 +638,14 @@
|
|||
|
||||
</div>
|
||||
@endif
|
||||
@if($user->getUserTotalCost()->total_user_cost > 0)
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-3">
|
||||
{{ trans('admin/users/table.total_assets_cost') }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{Helper::formatCurrencyOutput($user->getUserTotalCost()->total_user_cost)}}
|
||||
|
||||
<a id="optional_info" class="text-primary">
|
||||
<i class="fa fa-caret-right fa-2x" id="optional_info_icon"></i>
|
||||
<strong>{{ trans('admin/hardware/form.optional_infos') }}</strong>
|
||||
|
@ -658,13 +659,10 @@
|
|||
{{trans('general.accessories').': '.Helper::formatCurrencyOutput($user->getUserTotalCost()->accessory_cost)}}<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!--/.row-->
|
||||
@endif
|
||||
</div> <!--/end striped container-->
|
||||
</div> <!-- end col-md-9 -->
|
||||
|
||||
|
||||
|
||||
</div> <!--/.row-->
|
||||
</div><!-- /.tab-pane -->
|
||||
|
||||
|
@ -729,7 +727,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-5">{{ trans('general.name') }}</th>
|
||||
<th>{{ trans('admin/hardware/form.serial') }}</th>
|
||||
<th>{{ trans('admin/licenses/form.license_key') }}</th>
|
||||
<th data-footer-formatter="sumFormatter" data-fieldname="purchase_cost">{{ trans('general.purchase_cost') }}</th>
|
||||
<th>{{ trans('admin/licenses/form.purchase_order') }}</th>
|
||||
<th>{{ trans('general.order_number') }}</th>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\Support;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class Settings
|
||||
{
|
||||
|
@ -67,6 +68,39 @@ class Settings
|
|||
|
||||
}
|
||||
|
||||
public function enableLdap(): Settings
|
||||
{
|
||||
return $this->update([
|
||||
'ldap_enabled' => 1,
|
||||
'ldap_server' => 'ldaps://ldap.example.com',
|
||||
'ldap_uname' => 'fake_username',
|
||||
'ldap_pword' => Crypt::encrypt("fake_password"),
|
||||
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
|
||||
]);
|
||||
}
|
||||
|
||||
public function enableAnonymousLdap(): Settings
|
||||
{
|
||||
return $this->update([
|
||||
'ldap_enabled' => 1,
|
||||
'ldap_server' => 'ldaps://ldap.example.com',
|
||||
// 'ldap_uname' => 'fake_username',
|
||||
'ldap_pword' => Crypt::encrypt("fake_password"),
|
||||
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
|
||||
]);
|
||||
}
|
||||
|
||||
public function enableBadPasswordLdap(): Settings
|
||||
{
|
||||
return $this->update([
|
||||
'ldap_enabled' => 1,
|
||||
'ldap_server' => 'ldaps://ldap.example.com',
|
||||
'ldap_uname' => 'fake_username',
|
||||
'ldap_pword' => "badly_encrypted_password!",
|
||||
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes Attributes to modify in the application's settings.
|
||||
*/
|
||||
|
|
|
@ -46,7 +46,5 @@ class AssetMaintenanceTest extends TestCase
|
|||
$this->assertTrue($c->completion_date === null);
|
||||
$c->completion_date = '0000-00-00';
|
||||
$this->assertTrue($c->completion_date === null);
|
||||
$c->completion_date = '2017-05-12';
|
||||
$this->assertTrue($c->completion_date == Carbon::parse('2017-05-12'));
|
||||
}
|
||||
}
|
||||
|
|
210
tests/Unit/LdapTest.php
Normal file
210
tests/Unit/LdapTest.php
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Ldap;
|
||||
use Exception;
|
||||
use Tests\Support\InteractsWithSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LdapTest extends TestCase
|
||||
{
|
||||
use InteractsWithSettings;
|
||||
use \phpmock\phpunit\PHPMock;
|
||||
|
||||
public function testConnect()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
|
||||
$blah = Ldap::connectToLdap();
|
||||
$this->assertEquals('hello',$blah,"LDAP_connect should return 'hello'");
|
||||
}
|
||||
|
||||
// other test cases - with/without client-side certs?
|
||||
// with/without LDAP version 3?
|
||||
// with/without ignore cert validation?
|
||||
// test (and mock) ldap_start_tls() ?
|
||||
|
||||
public function testBindAdmin()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
|
||||
}
|
||||
|
||||
public function testBindBad()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(false);
|
||||
$this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
|
||||
$this->expectExceptionMessage("Could not bind to LDAP:");
|
||||
|
||||
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
|
||||
}
|
||||
// other test cases - test donked password?
|
||||
|
||||
public function testAnonymousBind()
|
||||
{
|
||||
//todo - would be nice to introspect somehow to make sure the right parameters were passed?
|
||||
$this->settings->enableAnonymousLdap();
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
|
||||
}
|
||||
|
||||
public function testBadAnonymousBind()
|
||||
{
|
||||
$this->settings->enableAnonymousLdap();
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(false);
|
||||
$this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
|
||||
$this->expectExceptionMessage("Could not bind to LDAP:");
|
||||
|
||||
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
|
||||
}
|
||||
|
||||
public function testBadEncryptedPassword()
|
||||
{
|
||||
$this->settings->enableBadPasswordLdap();
|
||||
|
||||
$this->expectExceptionMessage("Your app key has changed");
|
||||
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
|
||||
}
|
||||
|
||||
public function testFindAndBind()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_first_entry")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_get_attributes")->expects($this->once())->willReturn(
|
||||
[
|
||||
"count" => 1,
|
||||
0 => [
|
||||
'sn' => 'Surname',
|
||||
'firstName' => 'FirstName'
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$results = Ldap::findAndBindUserLdap("username","password");
|
||||
$this->assertEqualsCanonicalizing(["count" =>1,0 =>['sn' => 'Surname','firstname' => 'FirstName']],$results);
|
||||
}
|
||||
|
||||
public function testFindAndBindBadPassword()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
// note - we return FALSE first, to simulate a bad-bind, then TRUE the second time to simulate a successful admin bind
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->exactly(2))->willReturn(false, true);
|
||||
|
||||
// $this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
|
||||
|
||||
|
||||
// $this->expectExceptionMessage("exception");
|
||||
$results = Ldap::findAndBindUserLdap("username","password");
|
||||
$this->assertFalse($results);
|
||||
}
|
||||
|
||||
public function testFindAndBindCannotFindSelf()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(false);
|
||||
|
||||
$this->expectExceptionMessage("Could not search LDAP:");
|
||||
$results = Ldap::findAndBindUserLdap("username","password");
|
||||
$this->assertFalse($results);
|
||||
}
|
||||
|
||||
//maybe should do an AD test as well?
|
||||
|
||||
public function testFindLdapUsers()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(["stuff"]);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_parse_result")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_get_entries")->expects($this->once())->willReturn(["count" => 1]);
|
||||
|
||||
$results = Ldap::findLdapUsers();
|
||||
|
||||
$this->assertEqualsCanonicalizing(["count" => 1], $results);
|
||||
}
|
||||
|
||||
public function testFindLdapUsersPaginated()
|
||||
{
|
||||
$this->settings->enableLdap();
|
||||
|
||||
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
|
||||
$ldap_connect->expects($this->once())->willReturn('hello');
|
||||
|
||||
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
|
||||
$ldap_set_option->expects($this->exactly(3));
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->exactly(2))->willReturn(["stuff"]);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_parse_result")->expects($this->exactly(2))->willReturnCallback(
|
||||
function ($ldapconn, $search_results, $errcode , $matcheddn , $errmsg , $referrals, &$controls) {
|
||||
static $count = 0;
|
||||
if($count == 0) {
|
||||
$count++;
|
||||
$controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] = "cookie";
|
||||
return ["count" => 1];
|
||||
} else {
|
||||
$controls = [];
|
||||
return ["count" => 1];
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
$this->getFunctionMock("App\\Models", "ldap_get_entries")->expects($this->exactly(2))->willReturn(["count" => 1]);
|
||||
|
||||
$results = Ldap::findLdapUsers();
|
||||
|
||||
$this->assertEqualsCanonicalizing(["count" => 2], $results);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue