Merge branch 'develop' into scim_active_flag

This commit is contained in:
Brady Wetherington 2023-11-23 16:36:17 +00:00
commit 8916c976e1
37 changed files with 556 additions and 165 deletions

View file

@ -2988,6 +2988,15 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "bilias",
"name": "bilias",
"avatar_url": "https://avatars.githubusercontent.com/u/47315739?v=4",
"profile": "https://github.com/bilias",
"contributions": [
"code"
]
} }
] ]
} }

View file

@ -1,5 +1,5 @@
[![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) [![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-329-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) [![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 - Open Source Asset Management System ## Snipe-IT - Open Source Asset Management System
@ -146,6 +146,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | | [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | | [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | | [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View file

@ -754,34 +754,24 @@ class AssetsController extends Controller
*/ */
public function restore(Request $request, $assetId = null) public function restore(Request $request, $assetId = null)
{ {
// Get asset information
$asset = Asset::withTrashed()->find($assetId); if ($asset = Asset::withTrashed()->find($assetId)) {
$this->authorize('delete', $asset); $this->authorize('delete', $asset);
if (isset($asset->id)) { if ($asset->deleted_at == '') {
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.asset')])), 200);
if ($asset->deleted_at=='') {
$message = 'Asset was not deleted. No data was changed.';
} else {
$message = trans('admin/hardware/message.restore.success');
// Restore the asset
Asset::withTrashed()->where('id', $assetId)->restore();
$logaction = new Actionlog();
$logaction->item_type = Asset::class;
$logaction->item_id = $asset->id;
$logaction->created_at = date("Y-m-d H:i:s");
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restored');
} }
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset, $request), $message)); if ($asset->restore()) {
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/hardware/message.restore.success')), 200);
} }
// Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.asset'), 'error' => $asset->getErrors()->first()])), 200);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
} }
/** /**

View file

@ -263,14 +263,11 @@ class ConsumablesController extends Controller
// Make sure there is at least one available to checkout // Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0) { if ($consumable->numRemaining() <= 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable'))); return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/consumables/message.checkout.unavailable')));
\Log::debug('No enough remaining');
} }
// Make sure there is a valid category // Make sure there is a valid category
if (!$consumable->category){ if (!$consumable->category){
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')]))); return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.invalid_item_category_single', ['type' => trans('general.consumable')])));
return redirect()->route('consumables.index')->with('error', trans('general.invalid_item_category_single', ['type' => trans('general.consumable')]));
} }

View file

@ -6,9 +6,11 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Transformers\ManufacturersTransformer; use App\Http\Transformers\ManufacturersTransformer;
use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\SelectlistTransformer;
use App\Models\Actionlog;
use App\Models\Manufacturer; use App\Models\Manufacturer;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class ManufacturersController extends Controller class ManufacturersController extends Controller
@ -159,6 +161,44 @@ class ManufacturersController extends Controller
} }
/**
* Restore a given Manufacturer (mark as un-deleted)
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.3.4]
* @param int $id
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function restore($id)
{
$this->authorize('delete', Manufacturer::class);
if ($manufacturer = Manufacturer::withTrashed()->find($id)) {
if ($manufacturer->deleted_at == '') {
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.manufacturer')])), 200);
}
if ($manufacturer->restore()) {
$logaction = new Actionlog();
$logaction->item_type = Manufacturer::class;
$logaction->item_id = $manufacturer->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/manufacturers/message.restore.success')), 200);
}
// Check validation to make sure we're not restoring an item with the same unique attributes as a non-deleted one
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.manufacturer'), 'error' => $manufacturer->getErrors()->first()])), 200);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.does_not_exist')));
}
/** /**
* Gets a paginated collection for the select2 menus * Gets a paginated collection for the select2 menus
* *

View file

@ -11,6 +11,7 @@ use App\Http\Transformers\ConsumablesTransformer;
use App\Http\Transformers\LicensesTransformer; use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\UsersTransformer; use App\Http\Transformers\UsersTransformer;
use App\Models\Actionlog;
use App\Models\Asset; use App\Models\Asset;
use App\Models\Company; use App\Models\Company;
use App\Models\License; use App\Models\License;
@ -688,17 +689,31 @@ class UsersController extends Controller
*/ */
public function restore($userId = null) public function restore($userId = null)
{ {
// Get asset information
$user = User::withTrashed()->find($userId);
$this->authorize('delete', $user);
if (isset($user->id)) {
// Restore the user
User::withTrashed()->where('id', $userId)->restore();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.restored'))); if ($user = User::withTrashed()->find($userId)) {
$this->authorize('delete', $user);
if ($user->deleted_at == '') {
return response()->json(Helper::formatStandardApiResponse('error', trans('general.not_deleted', ['item_type' => trans('general.user')])), 200);
} }
$id = $userId; if ($user->restore()) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found', compact('id'))), 200);
$logaction = new Actionlog();
$logaction->item_type = User::class;
$logaction->item_id = $user->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
return response()->json(Helper::formatStandardApiResponse('success', trans('admin/users/message.restore.success')), 200);
}
// Check validation to make sure we're not restoring a user with the same username as an existing user
return response()->json(Helper::formatStandardApiResponse('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()])), 200);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')), 200);
} }
} }

View file

@ -4,7 +4,10 @@ namespace App\Http\Controllers;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel; use App\Models\AssetModel;
use App\Models\User;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Input;
@ -209,7 +212,7 @@ class AssetModelsController extends Controller
$this->authorize('delete', AssetModel::class); $this->authorize('delete', AssetModel::class);
// Check if the model exists // Check if the model exists
if (is_null($model = AssetModel::find($modelId))) { if (is_null($model = AssetModel::find($modelId))) {
return redirect()->route('models.index')->with('error', trans('admin/models/message.not_found')); return redirect()->route('models.index')->with('error', trans('admin/models/message.does_not_exist'));
} }
if ($model->assets()->count() > 0) { if ($model->assets()->count() > 0) {
@ -237,22 +240,42 @@ class AssetModelsController extends Controller
* *
* @author [A. Gianotto] [<snipe@snipe.net>] * @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0] * @since [v1.0]
* @param int $modelId * @param int $id
* @return Redirect * @return Redirect
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
public function getRestore($modelId = null) public function getRestore($id)
{ {
$this->authorize('create', AssetModel::class); $this->authorize('create', AssetModel::class);
// Get user information
$model = AssetModel::withTrashed()->find($modelId);
if (isset($model->id)) { if ($model = AssetModel::withTrashed()->find($id)) {
$model->restore();
if ($model->deleted_at == '') {
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.asset_model')]));
}
if ($model->restore()) {
$logaction = new Actionlog();
$logaction->item_type = User::class;
$logaction->item_id = $model->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
// Redirect them to the deleted page if there are more, otherwise the section index
$deleted_models = AssetModel::onlyTrashed()->count();
if ($deleted_models > 0) {
return redirect()->back()->with('success', trans('admin/models/message.restore.success'));
}
return redirect()->route('models.index')->with('success', trans('admin/models/message.restore.success')); return redirect()->route('models.index')->with('success', trans('admin/models/message.restore.success'));
} }
return redirect()->back()->with('error', trans('admin/models/message.not_found'));
// Check validation
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.asset_model'), 'error' => $model->getErrors()->first()]));
}
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
} }

View file

@ -6,6 +6,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog; use App\Models\Actionlog;
use App\Models\Manufacturer;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel; use App\Models\AssetModel;
@ -204,8 +205,9 @@ class AssetsController extends Controller
} }
if ($success) { if ($success) {
\Log::debug(e($asset->asset_tag));
return redirect()->route('hardware.index') return redirect()->route('hardware.index')
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => $asset->asset_tag])); ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => e($asset->asset_tag)]));
} }
@ -794,23 +796,26 @@ class AssetsController extends Controller
*/ */
public function getRestore($assetId = null) public function getRestore($assetId = null)
{ {
// Get asset information if ($asset = Asset::withTrashed()->find($assetId)) {
$asset = Asset::withTrashed()->find($assetId);
$this->authorize('delete', $asset); $this->authorize('delete', $asset);
if (isset($asset->id)) {
// Restore the asset
Asset::withTrashed()->where('id', $assetId)->restore();
$logaction = new Actionlog(); if ($asset->deleted_at == '') {
$logaction->item_type = Asset::class; return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.asset')]));
$logaction->item_id = $asset->id; }
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restored');
if ($asset->restore()) {
// Redirect them to the deleted page if there are more, otherwise the section index
$deleted_assets = Asset::onlyTrashed()->count();
if ($deleted_assets > 0) {
return redirect()->back()->with('success', trans('admin/hardware/message.restore.success'));
}
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success')); return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
} }
// Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.asset'), 'error' => $asset->getErrors()->first()]));
}
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist')); return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
} }

View file

@ -56,7 +56,6 @@ class LoginController extends Controller
parent::__construct(); parent::__construct();
$this->middleware('guest', ['except' => ['logout', 'postTwoFactorAuth', 'getTwoFactorAuth', 'getTwoFactorEnroll']]); $this->middleware('guest', ['except' => ['logout', 'postTwoFactorAuth', 'getTwoFactorAuth', 'getTwoFactorEnroll']]);
Session::put('backUrl', \URL::previous()); Session::put('backUrl', \URL::previous());
// $this->ldap = $ldap;
$this->saml = $saml; $this->saml = $saml;
} }
@ -82,7 +81,6 @@ class LoginController extends Controller
} }
if (Setting::getSettings()->login_common_disabled == '1') { if (Setting::getSettings()->login_common_disabled == '1') {
\Log::debug('login_common_disabled is set to 1 - return a 403');
return view('errors.403'); return view('errors.403');
} }
@ -123,7 +121,7 @@ class LoginController extends Controller
if ($user = Auth::user()) { if ($user = Auth::user()) {
$user->last_login = \Carbon::now(); $user->last_login = \Carbon::now();
$user->save(); $user->saveQuietly();
} }
} catch (\Exception $e) { } catch (\Exception $e) {
@ -199,7 +197,7 @@ class LoginController extends Controller
$user->email = $ldap_attr['email']; $user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname']; $user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc. $user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc.
$user->save(); $user->saveQuietly();
} // End if(!user) } // End if(!user)
return $user; return $user;
} }
@ -319,7 +317,7 @@ class LoginController extends Controller
if ($user = Auth::user()) { if ($user = Auth::user()) {
$user->last_login = \Carbon::now(); $user->last_login = \Carbon::now();
$user->activated = 1; $user->activated = 1;
$user->save(); $user->saveQuietly();
} }
// Redirect to the users page // Redirect to the users page
return redirect()->intended()->with('success', trans('auth/message.signin.success')); return redirect()->intended()->with('success', trans('auth/message.signin.success'));
@ -371,7 +369,7 @@ class LoginController extends Controller
[-2, -2, -2, -2] [-2, -2, -2, -2]
); );
$user->save(); // make sure to save *AFTER* displaying the barcode, or else we might save a two_factor_secret that we never actually displayed to the user if the barcode fails $user->saveQuietly(); // make sure to save *AFTER* displaying the barcode, or else we might save a two_factor_secret that we never actually displayed to the user if the barcode fails
return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj); return view('auth.two_factor_enroll')->with('barcode_obj', $barcode_obj);
} }
@ -426,7 +424,7 @@ class LoginController extends Controller
if (Google2FA::verifyKey($user->two_factor_secret, $secret)) { if (Google2FA::verifyKey($user->two_factor_secret, $secret)) {
$user->two_factor_enrolled = 1; $user->two_factor_enrolled = 1;
$user->save(); $user->saveQuietly();
$request->session()->put('2fa_authed', $user->id); $request->session()->put('2fa_authed', $user->id);
return redirect()->route('home')->with('success', 'You are logged in!'); return redirect()->route('home')->with('success', 'You are logged in!');

View file

@ -2,8 +2,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Manufacturer; use App\Models\Manufacturer;
use App\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -218,22 +222,37 @@ class ManufacturersController extends Controller
* @return Redirect * @return Redirect
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
*/ */
public function restore($manufacturers_id) public function restore($id)
{ {
$this->authorize('create', Manufacturer::class); $this->authorize('delete', Manufacturer::class);
$manufacturer = Manufacturer::onlyTrashed()->where('id', $manufacturers_id)->first();
if ($manufacturer) { if ($manufacturer = Manufacturer::withTrashed()->find($id)) {
if ($manufacturer->deleted_at == '') {
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.manufacturer')]));
}
// Not sure why this is necessary - it shouldn't fail validation here, but it fails without this, so....
$manufacturer->setValidating(false);
if ($manufacturer->restore()) { if ($manufacturer->restore()) {
$logaction = new Actionlog();
$logaction->item_type = Manufacturer::class;
$logaction->item_id = $manufacturer->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
// Redirect them to the deleted page if there are more, otherwise the section index
$deleted_manufacturers = Manufacturer::onlyTrashed()->count();
if ($deleted_manufacturers > 0) {
return redirect()->back()->with('success', trans('admin/manufacturers/message.success.restored'));
}
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.restore.success')); return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.restore.success'));
} }
return redirect()->back()->with('error', 'Could not restore.'); // Check validation to make sure we're not restoring an asset with the same asset tag (or unique attribute) as an existing asset
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.manufacturer'), 'error' => $manufacturer->getErrors()->first()]));
} }
return redirect()->back()->with('error', trans('admin/manufacturers/message.does_not_exist')); return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist'));
} }
} }

View file

@ -134,6 +134,7 @@ class ProfileController extends Controller
]; ];
$validator = \Validator::make($request->all(), $rules); $validator = \Validator::make($request->all(), $rules);
$validator->after(function ($validator) use ($request, $user) { $validator->after(function ($validator) use ($request, $user) {
if (! Hash::check($request->input('current_password'), $user->password)) { if (! Hash::check($request->input('current_password'), $user->password)) {
$validator->errors()->add('current_password', trans('validation.custom.hashed_pass')); $validator->errors()->add('current_password', trans('validation.custom.hashed_pass'));
@ -159,12 +160,14 @@ class ProfileController extends Controller
}); });
if (! $validator->fails()) { if (! $validator->fails()) {
$user->password = Hash::make($request->input('password')); $user->password = Hash::make($request->input('password'));
$user->save(); // We have to use saveQuietly here because for some reason this method was calling the User Oserver twice :(
$user->saveQuietly();
// Log the user out of other devices // Log the user out of other devices
Auth::logoutOtherDevices($request->input('password')); Auth::logoutOtherDevices($request->input('password'));
return redirect()->route('account.password.index')->with('success', 'Password updated!'); return redirect()->route('account')->with('success', trans('passwords.password_change'));
} }
return redirect()->back()->withInput()->withErrors($validator); return redirect()->back()->withInput()->withErrors($validator);

View file

@ -7,10 +7,10 @@ use App\Http\Controllers\Controller;
use App\Http\Controllers\UserNotFoundException; use App\Http\Controllers\UserNotFoundException;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use App\Http\Requests\SaveUserRequest; use App\Http\Requests\SaveUserRequest;
use App\Models\Actionlog;
use App\Models\Asset; use App\Models\Asset;
use App\Models\Company; use App\Models\Company;
use App\Models\Group; use App\Models\Group;
use App\Models\Ldap;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use App\Notifications\WelcomeNotification; use App\Notifications\WelcomeNotification;
@ -385,18 +385,35 @@ class UsersController extends Controller
*/ */
public function getRestore($id = null) public function getRestore($id = null)
{ {
$this->authorize('update', User::class); if ($user = User::withTrashed()->find($id)) {
// Get user information $this->authorize('delete', $user);
if (! User::onlyTrashed()->find($id)) {
return redirect()->route('users.index')->with('error', trans('admin/users/messages.user_not_found')); if ($user->deleted_at == '') {
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.user')]));
} }
// Restore the user if ($user->restore()) {
if (User::withTrashed()->where('id', $id)->restore()) { $logaction = new Actionlog();
$logaction->item_type = User::class;
$logaction->item_id = $user->id;
$logaction->created_at = date('Y-m-d H:i:s');
$logaction->user_id = Auth::user()->id;
$logaction->logaction('restore');
// Redirect them to the deleted page if there are more, otherwise the section index
$deleted_users = User::onlyTrashed()->count();
if ($deleted_users > 0) {
return redirect()->back()->with('success', trans('admin/users/message.success.restored'));
}
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.restored')); return redirect()->route('users.index')->with('success', trans('admin/users/message.success.restored'));
} }
return redirect()->route('users.index')->with('error', 'User could not be restored.'); // Check validation to make sure we're not restoring a user with the same username as an existing user
return redirect()->back()->with('error', trans('general.could_not_restore', ['item_type' => trans('general.user'), 'error' => $user->getErrors()->first()]));
}
return redirect()->route('users.index')->with('error', trans('admin/users/message.does_not_exist'));
} }
/** /**

View file

@ -73,7 +73,7 @@ class AssetModelsTransformer
$permissions_array['available_actions'] = [ $permissions_array['available_actions'] = [
'update' => (Gate::allows('update', AssetModel::class) && ($assetmodel->deleted_at == '')), 'update' => (Gate::allows('update', AssetModel::class) && ($assetmodel->deleted_at == '')),
'delete' => (Gate::allows('delete', AssetModel::class) && ($assetmodel->assets_count == 0)), 'delete' => $assetmodel->isDeletable(),
'clone' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at == '')), 'clone' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at == '')),
'restore' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at != '')), 'restore' => (Gate::allows('create', AssetModel::class) && ($assetmodel->deleted_at != '')),
]; ];

View file

@ -147,7 +147,7 @@ class AssetsTransformer
'clone' => Gate::allows('create', Asset::class) ? true : false, 'clone' => Gate::allows('create', Asset::class) ? true : false,
'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false, 'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false,
'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false, 'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false,
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class)) ? true : false, 'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class) && ($asset->deleted_at == '')) ? true : false,
]; ];

View file

@ -79,7 +79,7 @@ class UsersTransformer
$permissions_array['available_actions'] = [ $permissions_array['available_actions'] = [
'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')), 'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')),
'delete' => (Gate::allows('delete', User::class) && ($user->assets_count == 0) && ($user->licenses_count == 0) && ($user->accessories_count == 0)), 'delete' => $user->isDeletable(),
'clone' => (Gate::allows('create', User::class) && ($user->deleted_at == '')), 'clone' => (Gate::allows('create', User::class) && ($user->deleted_at == '')),
'restore' => (Gate::allows('create', User::class) && ($user->deleted_at != '')), 'restore' => (Gate::allows('create', User::class) && ($user->deleted_at != '')),
]; ];

View file

@ -69,7 +69,6 @@ class LogListener
$logaction->item()->associate($event->acceptance->checkoutable->license); $logaction->item()->associate($event->acceptance->checkoutable->license);
} }
\Log::debug('New onCheckoutAccepted Listener fired. logaction: '.print_r($logaction, true));
$logaction->save(); $logaction->save();
} }

View file

@ -6,6 +6,7 @@ use App\Models\Traits\Searchable;
use App\Presenters\Presentable; use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
@ -188,6 +189,21 @@ class AssetModel extends SnipeModel
return false; return false;
} }
/**
* Checks if the model is deletable
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.3.4]
* @return bool
*/
public function isDeletable()
{
return Gate::allows('delete', $this)
&& ($this->assets_count == 0)
&& ($this->deleted_at == '');
}
/** /**
* Get uploads for this model * Get uploads for this model
* *

View file

@ -100,7 +100,8 @@ class Category extends SnipeModel
{ {
return Gate::allows('delete', $this) return Gate::allows('delete', $this)
&& ($this->itemCount() == 0); && ($this->itemCount() == 0)
&& ($this->deleted_at == '');
} }
/** /**

View file

@ -77,7 +77,8 @@ class Manufacturer extends SnipeModel
&& ($this->assets()->count() === 0) && ($this->assets()->count() === 0)
&& ($this->licenses()->count() === 0) && ($this->licenses()->count() === 0)
&& ($this->consumables()->count() === 0) && ($this->consumables()->count() === 0)
&& ($this->accessories()->count() === 0); && ($this->accessories()->count() === 0)
&& ($this->deleted_at == '');
} }
public function assets() public function assets()

View file

@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\HasApiTokens; use Laravel\Passport\HasApiTokens;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
@ -201,6 +202,23 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return $this->checkPermissionSection('superuser'); return $this->checkPermissionSection('superuser');
} }
/**
* Checks if the user is deletable
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.3.4]
* @return bool
*/
public function isDeletable()
{
return Gate::allows('delete', $this)
&& ($this->assets()->count() === 0)
&& ($this->licenses()->count() === 0)
&& ($this->consumables()->count() === 0)
&& ($this->accessories()->count() === 0)
&& ($this->deleted_at == '');
}
/** /**
* Establishes the user -> company relationship * Establishes the user -> company relationship

View file

@ -22,6 +22,13 @@ class AssetObserver
$attributesOriginal = $asset->getRawOriginal(); $attributesOriginal = $asset->getRawOriginal();
$same_checkout_counter = false; $same_checkout_counter = false;
$same_checkin_counter = false; $same_checkin_counter = false;
$restoring_or_deleting = false;
// This is a gross hack to prevent the double logging when restoring an asset
if (array_key_exists('deleted_at', $attributes) && array_key_exists('deleted_at', $attributesOriginal)){
$restoring_or_deleting = (($attributes['deleted_at'] != $attributesOriginal['deleted_at']));
}
if (array_key_exists('checkout_counter', $attributes) && array_key_exists('checkout_counter', $attributesOriginal)){ if (array_key_exists('checkout_counter', $attributes) && array_key_exists('checkout_counter', $attributesOriginal)){
$same_checkout_counter = (($attributes['checkout_counter'] == $attributesOriginal['checkout_counter'])); $same_checkout_counter = (($attributes['checkout_counter'] == $attributesOriginal['checkout_counter']));
@ -36,7 +43,7 @@ class AssetObserver
if (($attributes['assigned_to'] == $attributesOriginal['assigned_to']) if (($attributes['assigned_to'] == $attributesOriginal['assigned_to'])
&& ($same_checkout_counter) && ($same_checkin_counter) && ($same_checkout_counter) && ($same_checkin_counter)
&& ((isset( $attributes['next_audit_date']) ? $attributes['next_audit_date'] : null) == (isset($attributesOriginal['next_audit_date']) ? $attributesOriginal['next_audit_date']: null)) && ((isset( $attributes['next_audit_date']) ? $attributes['next_audit_date'] : null) == (isset($attributesOriginal['next_audit_date']) ? $attributesOriginal['next_audit_date']: null))
&& ($attributes['last_checkout'] == $attributesOriginal['last_checkout'])) && ($attributes['last_checkout'] == $attributesOriginal['last_checkout']) && (!$restoring_or_deleting))
{ {
$changed = []; $changed = [];
@ -121,6 +128,22 @@ class AssetObserver
$logAction->logaction('delete'); $logAction->logaction('delete');
} }
/**
* Listen to the Asset deleting event.
*
* @param Asset $asset
* @return void
*/
public function restoring(Asset $asset)
{
$logAction = new Actionlog();
$logAction->item_type = Asset::class;
$logAction->item_id = $asset->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->user_id = Auth::id();
$logAction->logaction('restore');
}
/** /**
* Executes every time an asset is saved. * Executes every time an asset is saved.
* *

View file

@ -0,0 +1,149 @@
<?php
namespace App\Observers;
use App\Models\Actionlog;
use App\Models\User;
use Auth;
class UserObserver
{
/**
* Listen to the User updating event. This fires automatically every time an existing asset is saved.
*
* @param User $user
* @return void
*/
public function updating(User $user)
{
// ONLY allow these fields to be stored
$allowed_fields = [
'email',
'activated',
'first_name',
'last_name',
'website',
'country',
'gravatar',
'location_id',
'phone',
'jobtitle',
'manager_id',
'employee_num',
'username',
'notes',
'company_id',
'ldap_import',
'locale',
'two_factor_enrolled',
'two_factor_optin',
'department_id',
'address',
'address2',
'city',
'state',
'zip',
'remote',
'start_date',
'end_date',
'autoassign_licenses',
'vip',
'password'
];
$changed = [];
foreach ($user->getRawOriginal() as $key => $value) {
// Make sure the info is in the allow fields array
if (in_array($key, $allowed_fields)) {
// Check and see if the value changed
if ($user->getRawOriginal()[$key] != $user->getAttributes()[$key]) {
$changed[$key]['old'] = $user->getRawOriginal()[$key];
$changed[$key]['new'] = $user->getAttributes()[$key];
// Do not store the hashed password in changes
if ($key == 'password') {
$changed['password']['old'] = '*************';
$changed['password']['new'] = '*************';
}
}
}
}
if (count($changed) > 0) {
$logAction = new Actionlog();
$logAction->item_type = User::class;
$logAction->item_id = $user->id;
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
$logAction->target_id = $user->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->user_id = Auth::id();
$logAction->log_meta = json_encode($changed);
$logAction->logaction('update');
}
}
/**
* Listen to the User created event, and increment
* the next_auto_tag_base value in the settings table when i
* a new asset is created.
*
* @param User $user
* @return void
*/
public function created(User $user)
{
$logAction = new Actionlog();
$logAction->item_type = User::class; // can we instead say $logAction->item = $asset ?
$logAction->item_id = $user->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->user_id = Auth::id();
$logAction->logaction('create');
}
/**
* Listen to the User deleting event.
*
* @param User $user
* @return void
*/
public function deleting(User $user)
{
$logAction = new Actionlog();
$logAction->item_type = User::class;
$logAction->item_id = $user->id;
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
$logAction->target_id = $user->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->user_id = Auth::id();
$logAction->logaction('delete');
}
/**
* Listen to the User deleting event.
*
* @param User $user
* @return void
*/
public function restoring(User $user)
{
$logAction = new Actionlog();
$logAction->item_type = User::class;
$logAction->item_id = $user->id;
$logAction->target_type = User::class; // can we instead say $logAction->item = $asset ?
$logAction->target_id = $user->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->user_id = Auth::id();
$logAction->logaction('restore');
}
}

View file

@ -38,22 +38,63 @@ class ActionlogPresenter extends Presenter
public function icon() public function icon()
{ {
$itemicon = 'fas fa-paperclip';
if ($this->itemType() == 'asset') { // User related icons
return 'fas fa-barcode'; if ($this->itemType() == 'user') {
} elseif ($this->itemType() == 'accessory') {
return 'far fa-keyboard'; if ($this->actionType()=='create new') {
} elseif ($this->itemType() == 'consumable') { return 'fa-solid fa-user-plus';
return 'fas fa-tint'; }
} elseif ($this->itemType() == 'license') {
return 'far fa-save'; if ($this->actionType()=='merged') {
} elseif ($this->itemType() == 'component') {
return 'far fa-hdd';
} elseif ($this->itemType() == 'user') {
return 'fa-solid fa-people-arrows'; return 'fa-solid fa-people-arrows';
} }
if ($this->actionType()=='delete') {
return 'fa-solid fa-user-minus';
}
if ($this->actionType()=='delete') {
return 'fa-solid fa-user-minus';
}
if ($this->actionType()=='update') {
return 'fa-solid fa-user-pen';
}
return 'fa-solid fa-user';
}
// Everything else
if ($this->actionType()=='create new') {
return 'fa-solid fa-plus';
}
if ($this->actionType()=='delete') {
return 'fa-solid fa-user-xmark';
}
if ($this->actionType()=='update') {
return 'fa-solid fa-pen';
}
if ($this->actionType()=='restore') {
return 'fa-solid fa-trash-arrow-up';
}
if ($this->actionType()=='upload') {
return 'fas fa-paperclip';
}
if ($this->actionType()=='checkout') {
return 'fa-solid fa-rotate-left';
}
if ($this->actionType()=='checkin from') {
return 'fa-solid fa-rotate-right';
}
return 'fa-solid fa-rotate-right';
} }
public function actionType() public function actionType()

View file

@ -7,10 +7,12 @@ use App\Models\Asset;
use App\Models\Component; use App\Models\Component;
use App\Models\Consumable; use App\Models\Consumable;
use App\Models\License; use App\Models\License;
use App\Models\User;
use App\Models\Setting; use App\Models\Setting;
use App\Models\SnipeSCIMConfig; use App\Models\SnipeSCIMConfig;
use App\Observers\AccessoryObserver; use App\Observers\AccessoryObserver;
use App\Observers\AssetObserver; use App\Observers\AssetObserver;
use App\Observers\UserObserver;
use App\Observers\ComponentObserver; use App\Observers\ComponentObserver;
use App\Observers\ConsumableObserver; use App\Observers\ConsumableObserver;
use App\Observers\LicenseObserver; use App\Observers\LicenseObserver;
@ -58,6 +60,7 @@ class AppServiceProvider extends ServiceProvider
Schema::defaultStringLength(191); Schema::defaultStringLength(191);
Asset::observe(AssetObserver::class); Asset::observe(AssetObserver::class);
User::observe(UserObserver::class);
Accessory::observe(AccessoryObserver::class); Accessory::observe(AccessoryObserver::class);
Component::observe(ComponentObserver::class); Component::observe(ComponentObserver::class);
Consumable::observe(ConsumableObserver::class); Consumable::observe(ConsumableObserver::class);

View file

@ -1,10 +1,10 @@
<?php <?php
return array ( return array (
'app_version' => 'v6.2.3', 'app_version' => 'v6.2.4-pre',
'full_app_version' => 'v6.2.3 - build 11936-gb47e734b3', 'full_app_version' => 'v6.2.4-pre - build 12088-g780279a62',
'build_version' => '11936', 'build_version' => '12088',
'prerelease_version' => '', 'prerelease_version' => '',
'hash_version' => 'gb47e734b3', 'hash_version' => 'g780279a62',
'full_hash' => 'v6.2.3-175-gb47e734b3', 'full_hash' => 'v6.2.4-pre-327-g780279a62',
'branch' => 'develop', 'branch' => 'develop',
); );

66
package-lock.json generated
View file

@ -1565,27 +1565,27 @@
} }
}, },
"@types/eslint": { "@types/eslint": {
"version": "8.44.2", "version": "8.44.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz",
"integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==",
"requires": { "requires": {
"@types/estree": "*", "@types/estree": "*",
"@types/json-schema": "*" "@types/json-schema": "*"
} }
}, },
"@types/eslint-scope": { "@types/eslint-scope": {
"version": "3.7.4", "version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"requires": { "requires": {
"@types/eslint": "*", "@types/eslint": "*",
"@types/estree": "*" "@types/estree": "*"
} }
}, },
"@types/estree": { "@types/estree": {
"version": "1.0.1", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
}, },
"@types/express": { "@types/express": {
"version": "4.17.13", "version": "4.17.13",
@ -2078,9 +2078,9 @@
} }
}, },
"acorn": { "acorn": {
"version": "8.10.0", "version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w=="
}, },
"acorn-import-assertions": { "acorn-import-assertions": {
"version": "1.9.0", "version": "1.9.0",
@ -2201,9 +2201,9 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
}, },
"alpinejs": { "alpinejs": {
"version": "3.13.0", "version": "3.13.3",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
"integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==", "integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
"requires": { "requires": {
"@vue/reactivity": "~3.1.1" "@vue/reactivity": "~3.1.1"
} }
@ -4885,9 +4885,9 @@
} }
}, },
"es-module-lexer": { "es-module-lexer": {
"version": "1.3.0", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
"integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w=="
}, },
"es-to-primitive": { "es-to-primitive": {
"version": "1.2.1", "version": "1.2.1",
@ -15972,9 +15972,9 @@
} }
}, },
"jspdf-autotable": { "jspdf-autotable": {
"version": "3.5.31", "version": "3.7.1",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.31.tgz", "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.7.1.tgz",
"integrity": "sha512-Lc1KuLGDQWW/5t57Z/+c2E94XQV3jV2QVU3xMRiwvcm/nMx79aCkpPCsxLzJZVFneZvz4XoA8+egQR1QajYiWw==" "integrity": "sha512-5fgjqE8nIwUoNz5l/i/aD/uONKofE4yp/kJ097EKBllPVTPGnGV5OWHld30db3+CvNrgzrRl8gmTnKF6vag05g=="
}, },
"junk": { "junk": {
"version": "3.1.0", "version": "3.1.0",
@ -19644,9 +19644,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "5.88.2", "version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"requires": { "requires": {
"@types/eslint-scope": "^3.7.3", "@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0", "@types/estree": "^1.0.0",
@ -19685,18 +19685,18 @@
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
}, },
"@jridgewell/trace-mapping": { "@jridgewell/trace-mapping": {
"version": "0.3.19", "version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"requires": { "requires": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"@types/json-schema": { "@types/json-schema": {
"version": "7.0.12", "version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
}, },
"commander": { "commander": {
"version": "2.20.3", "version": "2.20.3",
@ -19727,9 +19727,9 @@
} }
}, },
"terser": { "terser": {
"version": "5.19.2", "version": "5.24.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
"requires": { "requires": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2", "acorn": "^8.8.2",

View file

@ -25,11 +25,11 @@
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.4.2", "@fortawesome/fontawesome-free": "^6.4.2",
"acorn": "^8.9.0", "acorn": "^8.11.2",
"acorn-import-assertions": "^1.9.0", "acorn-import-assertions": "^1.9.0",
"admin-lte": "^2.4.18", "admin-lte": "^2.4.18",
"ajv": "^6.12.6", "ajv": "^6.12.6",
"alpinejs": "^3.10.5", "alpinejs": "^3.13.2",
"blueimp-file-upload": "^9.34.0", "blueimp-file-upload": "^9.34.0",
"bootstrap": "^3.4.1", "bootstrap": "^3.4.1",
"bootstrap-colorpicker": "^2.5.3", "bootstrap-colorpicker": "^2.5.3",
@ -46,7 +46,7 @@
"jquery-ui": "^1.13.2", "jquery-ui": "^1.13.2",
"jquery-ui-bundle": "^1.12.1", "jquery-ui-bundle": "^1.12.1",
"jquery.iframe-transport": "^1.0.0", "jquery.iframe-transport": "^1.0.0",
"jspdf-autotable": "^3.5.30", "jspdf-autotable": "^3.7.1",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^5.0", "less-loader": "^5.0",
"list.js": "^1.5.0", "list.js": "^1.5.0",
@ -56,6 +56,6 @@
"tableexport.jquery.plugin": "1.28.0", "tableexport.jquery.plugin": "1.28.0",
"tether": "^1.4.0", "tether": "^1.4.0",
"vue-resource": "^1.5.2", "vue-resource": "^1.5.2",
"webpack": "^5.88.2" "webpack": "^5.89.0"
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -14,7 +14,7 @@
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=8ca888bbc050d9680cbb65021382acba", "/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=bdfc704731682c67645a2248b0b8d2d7",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb", "/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=e36e83c2aa3c3afdbb8ebe2c0309e91d", "/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-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-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/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
@ -33,9 +33,9 @@
"/js/build/vendor.js": "/js/build/vendor.js?id=917784d6fe54bcfe39656e0ded1b43e4", "/js/build/vendor.js": "/js/build/vendor.js?id=917784d6fe54bcfe39656e0ded1b43e4",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41", "/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41",
"/js/dist/all.js": "/js/dist/all.js?id=1a069f231b257b1ad4e3751302705f97", "/js/dist/all.js": "/js/dist/all.js?id=1a069f231b257b1ad4e3751302705f97",
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=07e52318da2cdf3171c4d88113f25fb6", "/js/dist/all-defer.js": "/js/dist/all-defer.js?id=7f9a130eda6916eaa32a0a57e81918f3",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397", "/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=e36e83c2aa3c3afdbb8ebe2c0309e91d", "/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.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=bdfc704731682c67645a2248b0b8d2d7",
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374", "/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374",

View file

@ -361,7 +361,7 @@ input[type=text], input[type=search] {
background-color: var(--back-sub); background-color: var(--back-sub);
} }
.table-striped>tbody>tr:nth-of-type(even){ .table-striped>tbody>tr:nth-of-type(even){
background-color: var(--back-sub-alt); background-color: var(--back-sub);
} }
#webui>div>div>div>div>div>table>tbody>tr>td>a>i.fa, .box-body, .box-footer, .box-header { #webui>div>div>div>div>div>table>tbody>tr>td>a>i.fa, .box-body, .box-footer, .box-header {
color: var(--text-main); color: var(--text-main);

View file

@ -72,6 +72,8 @@ return [
'consumable' => 'Consumable', 'consumable' => 'Consumable',
'consumables' => 'Consumables', 'consumables' => 'Consumables',
'country' => 'Country', 'country' => 'Country',
'could_not_restore' => 'Error restoring :item_type: :error',
'not_deleted' => 'The :item_type is not deleted so it cannot be restored',
'create' => 'Create New', 'create' => 'Create New',
'created' => 'Item Created', 'created' => 'Item Created',
'created_asset' => 'created asset', 'created_asset' => 'created asset',

View file

@ -5,4 +5,5 @@ return [
'user' => 'If a matching user with a valid email address exists in our system, a password recovery email has been sent.', 'user' => 'If a matching user with a valid email address exists in our system, a password recovery email has been sent.',
'token' => 'This password reset token is invalid or expired, or does not match the username provided.', 'token' => 'This password reset token is invalid or expired, or does not match the username provided.',
'reset' => 'Your password has been reset!', 'reset' => 'Your password has been reset!',
'password_change' => 'Your password has been updated!',
]; ];

View file

@ -10,6 +10,7 @@
@section('content') @section('content')
@if ($acceptances = \App\Models\CheckoutAcceptance::forUser(Auth::user())->pending()->count()) @if ($acceptances = \App\Models\CheckoutAcceptance::forUser(Auth::user())->pending()->count())
<div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="alert alert alert-warning fade in"> <div class="alert alert alert-warning fade in">
<i class="fas fa-exclamation-triangle faa-pulse animated"></i> <i class="fas fa-exclamation-triangle faa-pulse animated"></i>
@ -21,6 +22,7 @@
</strong> </strong>
</div> </div>
</div> </div>
</div>
@endif @endif
<div class="row"> <div class="row">
@ -388,7 +390,6 @@
data-show-columns="true" data-show-columns="true"
data-show-export="true" data-show-export="true"
data-show-footer="true" data-show-footer="true"
data-show-refresh="false"
data-sort-order="asc" data-sort-order="asc"
id="userAssets" id="userAssets"
class="table table-striped snipe-table" class="table table-striped snipe-table"
@ -405,6 +406,7 @@
<th class="col-md-2" data-switchable="true" data-visible="true">{{ trans('general.name') }}</th> <th class="col-md-2" data-switchable="true" data-visible="true">{{ trans('general.name') }}</th>
<th class="col-md-2" data-switchable="true" data-visible="true">{{ trans('admin/hardware/table.asset_model') }}</th> <th class="col-md-2" data-switchable="true" data-visible="true">{{ trans('admin/hardware/table.asset_model') }}</th>
<th class="col-md-3" data-switchable="true" data-visible="true">{{ trans('admin/hardware/table.serial') }}</th> <th class="col-md-3" data-switchable="true" data-visible="true">{{ trans('admin/hardware/table.serial') }}</th>
<th class="col-md-2" data-switchable="true" data-visible="false">{{ trans('admin/hardware/form.default_location') }}</th>
@can('self.view_purchase_cost') @can('self.view_purchase_cost')
<th class="col-md-6" data-footer-formatter="sumFormatter" data-fieldname="purchase_cost">{{ trans('general.purchase_cost') }}</th> <th class="col-md-6" data-footer-formatter="sumFormatter" data-fieldname="purchase_cost">{{ trans('general.purchase_cost') }}</th>
@endcan @endcan
@ -442,7 +444,7 @@
@endif @endif
</td> </td>
<td>{{ $asset->serial }}</td> <td>{{ $asset->serial }}</td>
<td>{{ ($asset->defaultLoc) ? $asset->defaultLoc->name : '' }}</td>
@can('self.view_purchase_cost') @can('self.view_purchase_cost')
<td> <td>
{!! Helper::formatCurrencyOutput($asset->purchase_cost) !!} {!! Helper::formatCurrencyOutput($asset->purchase_cost) !!}

View file

@ -279,9 +279,13 @@
+ ' data-title="{{ trans('general.delete') }}" onClick="return false;">' + ' data-title="{{ trans('general.delete') }}" onClick="return false;">'
+ '<i class="fas fa-trash" aria-hidden="true"></i><span class="sr-only">{{ trans('general.delete') }}</span></a>&nbsp;'; + '<i class="fas fa-trash" aria-hidden="true"></i><span class="sr-only">{{ trans('general.delete') }}</span></a>&nbsp;';
} else { } else {
// Do not show the delete button on things that are already deleted
if ((row.available_actions) && (row.available_actions.restore != true)) {
actions += '<span data-tooltip="true" title="{{ trans('general.cannot_be_deleted') }}"><a class="btn btn-danger btn-sm delete-asset disabled" onClick="return false;"><i class="fas fa-trash"></i></a></span>&nbsp;'; actions += '<span data-tooltip="true" title="{{ trans('general.cannot_be_deleted') }}"><a class="btn btn-danger btn-sm delete-asset disabled" onClick="return false;"><i class="fas fa-trash"></i></a></span>&nbsp;';
} }
}
if ((row.available_actions) && (row.available_actions.restore === true)) { if ((row.available_actions) && (row.available_actions.restore === true)) {
actions += '<form style="display: inline;" method="POST" action="{{ config('app.url') }}/' + dest + '/' + row.id + '/restore"> '; actions += '<form style="display: inline;" method="POST" action="{{ config('app.url') }}/' + dest + '/' + row.id + '/restore"> ';

View file

@ -706,6 +706,13 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
] ]
)->name('api.manufacturers.selectlist'); )->name('api.manufacturers.selectlist');
Route::post('{id}/restore',
[
Api\ManufacturersController::class,
'restore'
]
)->name('api.manufacturers.restore');
}); });
Route::resource('manufacturers', Route::resource('manufacturers',
@ -742,6 +749,13 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
] ]
)->name('api.models.assets'); )->name('api.models.assets');
Route::post('{id}/restore',
[
Api\AssetModelsController::class,
'restore'
]
)->name('api.models.restore');
}); });
Route::resource('models', Route::resource('models',