diff --git a/.all-contributorsrc b/.all-contributorsrc index 4b205114c..37d93b9ed 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -829,6 +829,15 @@ "test", "code" ] + }, + { + "login": "Gelob", + "name": "Ryan", + "avatar_url": "https://avatars3.githubusercontent.com/u/422752?v=4", + "profile": "https://github.com/Gelob", + "contributions": [ + "doc" + ] } ] } diff --git a/README.md b/README.md index 0e6326a45..dff4a3481 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Build Status](https://travis-ci.org/snipe/snipe-it.svg?branch=develop)](https://travis-ci.org/snipe/snipe-it) [![Stories in Ready](https://badge.waffle.io/snipe/snipe-it.png?label=ready+for+dev&title=Ready+for+development)](http://waffle.io/snipe/snipe-it) [![Maintenance](https://img.shields.io/maintenance/yes/2017.svg)]() [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![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/snipeyhead.svg?style=social)](https://twitter.com/snipeyhead) [![Zenhub](https://img.shields.io/badge/Shipping_faster_with-ZenHub-5e60ba.svg)](https://zenhub.io) [![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-89-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-90-orange.svg?style=flat-square)](#contributors) ## Snipe-IT - Open Source Asset Management System @@ -68,7 +68,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken | [
Gil Rutkowski](http://FlashingCursor.com)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=flashingcursor "Code") | [
Desmond Morris](http://www.desmondmorris.com)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=desmondmorris "Code") | [
Nick Peelman](http://peelman.us)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=peelman "Code") | [
Abraham Vegh](https://abrahamvegh.com)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=abrahamvegh "Code") | [
Mohamed Rashid](https://github.com/rashivkp)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=rashivkp "Documentation") | [
Kasey](http://hinchk.github.io)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=HinchK "Code") | [
Brett](https://github.com/BrettFagerlund)
[⚠️](https://github.com/snipe/snipe-it/commits?author=BrettFagerlund "Tests") | | [
Jason Spriggs](http://jasonspriggs.com)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=jasonspriggs "Code") | [
Nate Felton](http://n8felton.wordpress.com)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=n8felton "Code") | [
Manasses Ferreira](http://homepages.dcc.ufmg.br/~manassesferreira)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=manassesferreira "Code") | [
Steve](https://github.com/steveelwood)
[⚠️](https://github.com/snipe/snipe-it/commits?author=steveelwood "Tests") | [
matc](http://twitter.com/matc)
[⚠️](https://github.com/snipe/snipe-it/commits?author=matc "Tests") | [
Cole R. Davis](http://www.davisracingteam.com)
[⚠️](https://github.com/snipe/snipe-it/commits?author=VanillaNinjaD "Tests") | [
gibsonjoshua55](https://github.com/gibsonjoshua55)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=gibsonjoshua55 "Code") | | [
Robin Temme](https://github.com/zwerch)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=zwerch "Code") | [
Iman](https://github.com/imanghafoori1)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=imanghafoori1 "Code") | [
Richard Hofman](https://github.com/richardhofman6)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=richardhofman6 "Code") | [
gizzmojr](https://github.com/gizzmojr)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=gizzmojr "Code") | [
Jenny Li](https://github.com/imjennyli)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=imjennyli "Documentation") | [
Geoff Young](https://github.com/GeoffYoung)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=GeoffYoung "Code") | [
Elliot Blackburn](http://www.elliotblackburn.com)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=BlueHatbRit "Documentation") | -| [
TΓ΅nis Ormisson](http://andmemasin.eu)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [
Nicolai Essig](http://www.nicolai-essig.de)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [
Danielle](https://github.com/techincolor)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [
Lawrence](https://github.com/TheVakman)
[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [πŸ›](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [
uknzaeinozpas](https://github.com/uknzaeinozpas)
[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [πŸ’»](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | +| [
TΓ΅nis Ormisson](http://andmemasin.eu)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=TonisOrmisson "Code") | [
Nicolai Essig](http://www.nicolai-essig.de)
[πŸ’»](https://github.com/snipe/snipe-it/commits?author=thakilla "Code") | [
Danielle](https://github.com/techincolor)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=techincolor "Documentation") | [
Lawrence](https://github.com/TheVakman)
[⚠️](https://github.com/snipe/snipe-it/commits?author=TheVakman "Tests") [πŸ›](https://github.com/snipe/snipe-it/issues?q=author%3ATheVakman "Bug reports") | [
uknzaeinozpas](https://github.com/uknzaeinozpas)
[⚠️](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Tests") [πŸ’»](https://github.com/snipe/snipe-it/commits?author=uknzaeinozpas "Code") | [
Ryan](https://github.com/Gelob)
[πŸ“–](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 3976d473d..d41d2c093 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -72,6 +72,8 @@ class AssetsController extends Controller 'updated_at', 'purchase_date', 'purchase_cost', + 'last_audit_date', + 'next_audit_date', 'warranty_months', ]; @@ -675,7 +677,11 @@ class AssetsController extends Controller if ($asset) { + // We don't want to log this as a normal update, so let's bypass that + $asset->unsetEventDispatcher(); $asset->next_audit_date = $request->input('next_audit_date'); + $asset->last_audit_date = date('Y-m-d h:i:s'); + if ($asset->save()) { $log = $asset->logAudit(request('note'),request('location_id')); return response()->json(Helper::formatStandardApiResponse('success', [ diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 84f38fe15..27bb31fbf 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -68,11 +68,11 @@ class UsersController extends Controller } if ($request->has('company_id')) { - $users = $users->where('company_id', '=', $request->input('company_id')); + $users = $users->where('users.company_id', '=', $request->input('company_id')); } if ($request->has('location_id')) { - $users = $users->where('location_id', '=', $request->input('location_id')); + $users = $users->where('users.location_id', '=', $request->input('location_id')); } if ($request->has('group_id')) { diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php index ffbe5ad40..a2598dca6 100755 --- a/app/Http/Controllers/AssetsController.php +++ b/app/Http/Controllers/AssetsController.php @@ -402,13 +402,6 @@ class AssetsController extends Controller $asset->delete(); - $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('deleted'); - return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success')); } @@ -919,6 +912,14 @@ class AssetsController extends Controller if (isset($asset->id)) { // 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 redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success')); } return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist')); @@ -1255,6 +1256,7 @@ class AssetsController extends Controller return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list'); } + public function auditStore(Request $request, $id) { $this->authorize('audit', Asset::class); @@ -1270,7 +1272,11 @@ class AssetsController extends Controller } $asset = Asset::findOrFail($id); + // We don't want to log this as a normal update, so let's bypass that + $asset->unsetEventDispatcher(); + $asset->next_audit_date = $request->input('next_audit_date'); + $asset->last_audit_date = date('Y-m-d h:i:s'); if ($asset->save()) { $asset->logAudit(request('note'), request('location_id')); diff --git a/app/Http/Controllers/LicensesController.php b/app/Http/Controllers/LicensesController.php index 444783677..559f979fa 100755 --- a/app/Http/Controllers/LicensesController.php +++ b/app/Http/Controllers/LicensesController.php @@ -362,7 +362,14 @@ class LicensesController extends Controller // Redirect to the asset management page with error return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found')); } - $this->authorize('checkin', $licenseSeat); + + if (is_null($license = License::find($licenseSeat->license_id))) { + // Redirect to the asset management page with error + return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found')); + } + + + $this->authorize('checkout', $license); return view('licenses/checkin', compact('licenseSeat'))->with('backto', $backTo); } @@ -386,8 +393,7 @@ class LicensesController extends Controller } $license = License::find($licenseSeat->license_id); - - $this->authorize('checkin', $licenseSeat); + $this->authorize('checkout', $license); if (!$license->reassignable) { // Not allowed to checkin diff --git a/app/Http/Controllers/ManufacturersController.php b/app/Http/Controllers/ManufacturersController.php index c936b90e5..39a8f7acf 100755 --- a/app/Http/Controllers/ManufacturersController.php +++ b/app/Http/Controllers/ManufacturersController.php @@ -35,6 +35,7 @@ class ManufacturersController extends Controller */ public function index() { + $this->authorize('index', Manufacturer::class); return view('manufacturers/index', compact('manufacturers')); } @@ -49,6 +50,7 @@ class ManufacturersController extends Controller */ public function create() { + $this->authorize('create', Manufacturer::class); return view('manufacturers/edit')->with('item', new Manufacturer); } @@ -65,6 +67,7 @@ class ManufacturersController extends Controller public function store(ImageUploadRequest $request) { + $this->authorize('edit', Manufacturer::class); $manufacturer = new Manufacturer; $manufacturer->name = $request->input('name'); $manufacturer->user_id = Auth::user()->id; @@ -104,6 +107,7 @@ class ManufacturersController extends Controller */ public function edit($id = null) { + $this->authorize('edit', Manufacturer::class); // Check if the manufacturer exists if (is_null($item = Manufacturer::find($id))) { return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.does_not_exist')); @@ -125,6 +129,7 @@ class ManufacturersController extends Controller */ public function update(ImageUploadRequest $request, $manufacturerId = null) { + $this->authorize('edit', Manufacturer::class); // Check if the manufacturer exists if (is_null($manufacturer = Manufacturer::find($manufacturerId))) { // Redirect to the manufacturer page @@ -186,6 +191,7 @@ class ManufacturersController extends Controller */ public function destroy($manufacturerId) { + $this->authorize('delete', Manufacturer::class); // Check if the manufacturer exists if (is_null($manufacturer = Manufacturer::find($manufacturerId))) { // Redirect to the manufacturers page @@ -224,6 +230,7 @@ class ManufacturersController extends Controller */ public function show($manufacturerId = null) { + $this->authorize('view', Manufacturer::class); $manufacturer = Manufacturer::find($manufacturerId); if (isset($manufacturer->id)) { diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index ba93c0da4..834aa91db 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -535,6 +535,14 @@ class ReportsController extends Controller $header[] = trans('general.updated_at'); } + if ($request->has('last_audit_date')) { + $header[] = trans('general.last_audit'); + } + + if ($request->has('next_audit_date')) { + $header[] = trans('general.next_audit_date'); + } + if ($request->has('notes')) { $header[] = trans('general.notes'); } @@ -709,6 +717,14 @@ class ReportsController extends Controller $row[] = ($asset->updated_at) ? $asset->updated_at : ''; } + if ($request->has('last_audit_date')) { + $row[] = ($asset->last_audit_date) ? $asset->last_audit_date : ''; + } + + if ($request->has('next_audit_date')) { + $row[] = ($asset->next_audit_date) ? $asset->next_audit_date : ''; + } + if ($request->has('notes')) { $row[] = ($asset->notes) ? $asset->notes : ''; } diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index 0f8109dc1..c37a53478 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -69,6 +69,8 @@ class AssetsTransformer 'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null, 'created_at' => Helper::getFormattedDateObject($asset->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($asset->updated_at, 'datetime'), + 'last_audit_date' => Helper::getFormattedDateObject($asset->last_audit_date, 'datetime'), + 'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'), 'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'), 'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'), 'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'), diff --git a/app/Models/Asset.php b/app/Models/Asset.php index fe3589817..6ecc883a3 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -49,7 +49,9 @@ class Asset extends Depreciable 'deleted_at', 'purchase_date', 'last_checkout', - 'expected_checkin' + 'expected_checkin', + 'last_audit_date', + 'next_audit_date' ]; @@ -68,6 +70,7 @@ class Asset extends Depreciable 'status' => 'integer', 'purchase_cost' => 'numeric|nullable', 'next_audit_date' => 'date|nullable', + 'last_audit_date' => 'date|nullable', ]; /** diff --git a/app/Models/User.php b/app/Models/User.php index 4cb790e28..b1de55443 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use App\Http\Traits\UniqueUndeletedTrait; use Illuminate\Notifications\Notifiable; use Laravel\Passport\HasApiTokens; +use DB; class User extends SnipeModel implements AuthenticatableContract, CanResetPasswordContract { @@ -441,7 +442,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo //Ugly, ugly code because Laravel sucks at self-joins ->orWhere(function ($query) use ($search) { - $query->whereRaw("users.manager_id IN (select id from users where first_name LIKE ? OR last_name LIKE ?)", ["%$search%", "%$search%"]); + $query->whereRaw(DB::getTablePrefix()."users.manager_id IN (select id from ".DB::getTablePrefix()."users where first_name LIKE ? OR last_name LIKE ?)", ["%$search%", "%$search%"]); }); diff --git a/app/Policies/ManufacturerPolicy.php b/app/Policies/ManufacturerPolicy.php new file mode 100644 index 000000000..8800b46c3 --- /dev/null +++ b/app/Policies/ManufacturerPolicy.php @@ -0,0 +1,13 @@ + false, "title" => trans('admin/hardware/form.expected_checkin'), "formatter" => "dateDisplayFormatter" + ], [ + "field" => "last_audit_date", + "searchable" => false, + "sortable" => true, + "visible" => false, + "title" => trans('general.last_audit'), + "formatter" => "dateDisplayFormatter" + ], [ + "field" => "next_audit_date", + "searchable" => false, + "sortable" => true, + "visible" => false, + "title" => trans('general.next_audit_date'), + "formatter" => "dateDisplayFormatter" ], ]; diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 264bce970..da21b1c7c 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -14,6 +14,7 @@ use App\Models\License; use App\Models\Location; use App\Models\Statuslabel; use App\Models\Supplier; +use App\Models\Manufacturer; use App\Models\User; use App\Policies\AccessoryPolicy; use App\Policies\AssetModelPolicy; @@ -28,6 +29,7 @@ use App\Policies\LocationPolicy; use App\Policies\StatuslabelPolicy; use App\Policies\SupplierPolicy; use App\Policies\UserPolicy; +use App\Policies\ManufacturerPolicy; use Carbon\Carbon; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; @@ -38,6 +40,8 @@ class AuthServiceProvider extends ServiceProvider /** * The policy mappings for the application. * + * See SnipePermissionsPolicy for additional information. + * * @var array */ protected $policies = [ @@ -54,6 +58,7 @@ class AuthServiceProvider extends ServiceProvider Statuslabel::class => StatuslabelPolicy::class, Supplier::class => SupplierPolicy::class, User::class => UserPolicy::class, + Manufacturer::class => ManufacturerPolicy::class, ]; /** @@ -126,6 +131,7 @@ class AuthServiceProvider extends ServiceProvider || $user->can('view', \App\Models\Department::class) || $user->can('view', \App\Models\Location::class) || $user->can('view', \App\Models\Company::class) + || $user->can('view', \App\Models\Manufacturer::class) || $user->can('view', \App\Models\Depreciation::class); }); } diff --git a/config/version.php b/config/version.php index 9ed0644a2..e86d97ffc 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ 'v4.1.8-pre', - 'full_app_version' => 'v4.1.8-pre - build 3034', - 'build_version' => '3034', + 'full_app_version' => 'v4.1.8-pre - build 3068-gceca76b', + 'build_version' => '3068', 'prerelease_version' => '', - 'hash_version' => 'g4f3c932', - 'full_hash' => 'v4.1.7-37-g4f3c932', + 'hash_version' => 'gceca76b', + 'full_hash' => 'v4.1.7-34-gceca76b', 'branch' => 'master', ); diff --git a/database/migrations/2017_12_12_010457_normalize_asset_last_audit_date.php b/database/migrations/2017_12_12_010457_normalize_asset_last_audit_date.php new file mode 100644 index 000000000..cf80c2faa --- /dev/null +++ b/database/migrations/2017_12_12_010457_normalize_asset_last_audit_date.php @@ -0,0 +1,49 @@ +datetime('last_audit_date')->after('assigned_type')->nullable()->default(null); + }); + + // Grab the latest info from the Actionlog table where the action is 'audit' + $audits = Actionlog::selectRaw('MAX(created_at) AS created_at, item_id')->where('action_type', 'audit')->where('item_type', Asset::class)->groupBy('item_id')->orderBy('created_at', 'desc')->get(); + + if ($audits) { + foreach ($audits as $audit) { + $assets = Asset::where('id', $audit->item_id)->first(); + $assets->last_audit_date = $audit->created_at; + $assets->unsetEventDispatcher(); + $assets->save(); + } + } + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('assets', function (Blueprint $table) { + $table->dropColumn('last_audit_date'); + }); + } +} diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index b50afe081..94acace65 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -151,6 +151,7 @@ 'recent_activity' => 'Recent Activity', 'remove_company' => 'Remove Company Association', 'reports' => 'Reports', + 'restored' => 'restored', 'requested' => 'Requested', 'request_canceled' => 'Request Canceled', 'save' => 'Save', diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index a55de3839..6ee49939d 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -32,19 +32,22 @@ {{-- Page content --}} @section('content')
+ + @if ($asset->deleted_at!='') +
+
+ + WARNING: + This asset has been deleted. + You must restore it before you can assign it to someone. +
+
+ @endif +
- @if ($asset->deleted_at!='') -
-
- - WARNING: - This asset has been deleted. - You must restore it before you can assign it to someone. -
-
- @endif +