diff --git a/.gitignore b/.gitignore index 61e7405ae..d158248e1 100755 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ public/uploads/logo.png public/uploads/logo.svg public/uploads/models/* public/uploads/suppliers/* +public/uploads/accessories/* public/uploads/users/* storage/app/private_uploads/users/* storage/debugbar/ diff --git a/app/Exceptions/CheckoutNotAllowed.php b/app/Exceptions/CheckoutNotAllowed.php index 74c65efb1..dc80a44f4 100644 --- a/app/Exceptions/CheckoutNotAllowed.php +++ b/app/Exceptions/CheckoutNotAllowed.php @@ -7,6 +7,6 @@ class CheckoutNotAllowed extends Exception { public function __toString() { - "A checkout is not allowed under these circumstances"; + return "A checkout is not allowed under these circumstances"; } } diff --git a/app/Http/Controllers/AccessoriesController.php b/app/Http/Controllers/AccessoriesController.php index c2fa92bc6..10d28b101 100755 --- a/app/Http/Controllers/AccessoriesController.php +++ b/app/Http/Controllers/AccessoriesController.php @@ -18,6 +18,8 @@ use Illuminate\Http\Request; use Slack; use Str; use View; +use Image; +use App\Http\Requests\ImageUploadRequest; /** This controller handles all actions related to Accessories for * the Snipe-IT Asset Management application. @@ -57,6 +59,7 @@ class AccessoriesController extends Controller ->with('item', new Accessory) ->with('category_list', Helper::categoryList('accessory')) ->with('company_list', Helper::companyList()) + ->with('supplier_list', Helper::suppliersList()) ->with('location_list', Helper::locationsList()) ->with('manufacturer_list', Helper::manufacturerList()); } @@ -68,7 +71,7 @@ class AccessoriesController extends Controller * @author [A. Gianotto] [] * @return Redirect */ - public function store(Request $request) + public function store(ImageUploadRequest $request) { $this->authorize(Accessory::class); // create a new model instance @@ -87,6 +90,28 @@ class AccessoriesController extends Controller $accessory->purchase_cost = Helper::ParseFloat(request('purchase_cost')); $accessory->qty = request('qty'); $accessory->user_id = Auth::user()->id; + $accessory->supplier_id = request('supplier_id'); + + if ($request->hasFile('image')) { + + if (!config('app.lock_passwords')) { + $image = $request->file('image'); + $ext = $image->getClientOriginalExtension(); + $file_name = "accessory-".str_random(18).'.'.$ext; + $path = public_path('/uploads/accessories'); + if ($image->getClientOriginalExtension()!='svg') { + Image::make($image->getRealPath())->resize(null, 250, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + })->save($path.'/'.$file_name); + } else { + $image->move($path, $file_name); + } + $accessory->image = $file_name; + } + } + + // Was the accessory created? if ($accessory->save()) { @@ -116,6 +141,7 @@ class AccessoriesController extends Controller ->with('category_list', Helper::categoryList('accessory')) ->with('company_list', Helper::companyList()) ->with('location_list', Helper::locationsList()) + ->with('supplier_list', Helper::suppliersList()) ->with('manufacturer_list', Helper::manufacturerList()); } @@ -127,7 +153,7 @@ class AccessoriesController extends Controller * @param int $accessoryId * @return Redirect */ - public function update(Request $request, $accessoryId = null) + public function update(ImageUploadRequest $request, $accessoryId = null) { if (is_null($accessory = Accessory::find($accessoryId))) { return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist')); @@ -144,11 +170,38 @@ class AccessoriesController extends Controller $accessory->manufacturer_id = request('manufacturer_id'); $accessory->order_number = request('order_number'); $accessory->model_number = request('model_number'); - $accessory->purchase_date = request('purchase_date'); - $accessory->purchase_cost = request('purchase_cost'); + $accessory->purchase_date = request('purchase_date'); + $accessory->purchase_cost = request('purchase_cost'); $accessory->qty = request('qty'); + $accessory->supplier_id = request('supplier_id'); - // Was the accessory updated? + if ($request->hasFile('image')) { + + if (!config('app.lock_passwords')) { + + + $image = $request->file('image'); + $ext = $image->getClientOriginalExtension(); + $file_name = "accessory-".str_random(18).'.'.$ext; + $path = public_path('/uploads/accessories'); + if ($image->getClientOriginalExtension()!='svg') { + Image::make($image->getRealPath())->resize(null, 250, function ($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + })->save($path.'/'.$file_name); + } else { + $image->move($path, $file_name); + } + if (($accessory->image) && (file_exists($path.'/'.$accessory->image))) { + unlink($path.'/'.$accessory->image); + } + + $accessory->image = $file_name; + } + } + + + // Was the accessory updated? if ($accessory->save()) { return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success')); } diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index 97aaf2c4b..aaa514009 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -38,6 +38,10 @@ class AccessoriesController extends Controller $accessories->where('manufacturer_id','=',$request->input('manufacturer_id')); } + if ($request->has('supplier_id')) { + $accessories->where('supplier_id','=',$request->input('supplier_id')); + } + $offset = $request->input('offset', 0); $limit = $request->input('limit', 50); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 8d1fe7b33..6b005c2bc 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -457,16 +457,28 @@ class AssetsController extends Controller $this->authorize('checkout', $asset); + $error_payload = []; + $error_payload['asset'] = [ + 'id' => $asset->id, + 'asset_tag' => $asset->asset_tag, + ]; if ($request->has('user_id')) { $target = User::find($request->input('user_id')); + $error_payload['target_id'] = $request->input('user_id'); + $error_payload['target_type'] = User::class; + // Don't let the user check an asset out to itself } elseif ($request->has('asset_id')) { - $target = Asset::find($request->input('asset_id')); + $target = Asset::where('id','!=',$asset_id)->find($request->input('asset_id')); + $error_payload['target_id'] = $request->input('asset_id'); + $error_payload['target_type'] = Asset::class; } elseif ($request->has('location_id')) { $target = Location::find($request->input('location_id')); + $error_payload['target_id'] = $request->input('location_id'); + $error_payload['target_type'] = Location::class; } if (!isset($target)) { - return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], 'No valid checkout target specified for asset '.e($asset->asset_tag).'.')); + return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'No valid checkout target specified for asset '.e($asset->asset_tag).'.')); } $checkout_at = request('checkout_at', date("Y-m-d H:i:s")); diff --git a/app/Http/Controllers/Api/CategoriesController.php b/app/Http/Controllers/Api/CategoriesController.php index 82f34754a..58f6dd0a1 100644 --- a/app/Http/Controllers/Api/CategoriesController.php +++ b/app/Http/Controllers/Api/CategoriesController.php @@ -20,7 +20,7 @@ class CategoriesController extends Controller public function index(Request $request) { $this->authorize('view', Category::class); - $allowed_columns = ['id', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email']; + $allowed_columns = ['id', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email', 'assets_count', 'accessories_count', 'consumables_count', 'components_count']; $categories = Category::select(['id', 'created_at', 'updated_at', 'name','category_type','use_default_eula','eula_text', 'require_acceptance','checkin_email']) ->withCount('assets', 'accessories', 'consumables', 'components'); @@ -32,7 +32,7 @@ class CategoriesController extends Controller $offset = $request->input('offset', 0); $limit = $request->input('limit', 50); $order = $request->input('order') === 'asc' ? 'asc' : 'desc'; - $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at'; + $sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'assets_count'; $categories->orderBy($sort, $order); $total = $categories->count(); diff --git a/app/Http/Controllers/Api/StatuslabelsController.php b/app/Http/Controllers/Api/StatuslabelsController.php index 045d587bd..9860beea2 100644 --- a/app/Http/Controllers/Api/StatuslabelsController.php +++ b/app/Http/Controllers/Api/StatuslabelsController.php @@ -22,7 +22,7 @@ class StatuslabelsController extends Controller public function index(Request $request) { $this->authorize('view', Statuslabel::class); - $allowed_columns = ['id','name','created_at']; + $allowed_columns = ['id','name','created_at', 'assets_count']; $statuslabels = Statuslabel::withCount('assets'); @@ -137,8 +137,14 @@ class StatuslabelsController extends Controller $this->authorize('delete', Statuslabel::class); $statuslabel = Statuslabel::findOrFail($id); $this->authorize('delete', $statuslabel); - $statuslabel->delete(); - return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success'))); + + // Check that there are no assets associated + if ($statuslabel->assets()->count() == 0) { + $statuslabel->delete(); + return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/statuslabels/message.delete.success'))); + } + + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/statuslabels/message.assoc_assets'))); } diff --git a/app/Http/Controllers/Api/SuppliersController.php b/app/Http/Controllers/Api/SuppliersController.php index 99a60c6a9..063f82438 100644 --- a/app/Http/Controllers/Api/SuppliersController.php +++ b/app/Http/Controllers/Api/SuppliersController.php @@ -20,11 +20,11 @@ class SuppliersController extends Controller public function index(Request $request) { $this->authorize('view', Supplier::class); - $allowed_columns = ['id','name','address','phone','contact','fax','email']; + $allowed_columns = ['id','name','address','phone','contact','fax','email','image','assets_count','licenses_count', 'accessories_count']; $suppliers = Supplier::select( array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at') - )->withCount('assets')->withCount('licenses')->whereNull('deleted_at'); + )->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at'); if ($request->has('search')) { diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php index 0e91949fb..01fc3d55c 100755 --- a/app/Http/Controllers/AssetsController.php +++ b/app/Http/Controllers/AssetsController.php @@ -458,7 +458,7 @@ class AssetsController extends Controller if (request('assigned_user')) { $target = User::find(request('assigned_user')); } elseif (request('assigned_asset')) { - $target = Asset::find(request('assigned_asset')); + $target = Asset::where('id','!=',$assetId)->find(request('assigned_asset')); } elseif (request('assigned_location')) { $target = Location::find(request('assigned_location')); } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 2cbf5cd28..1d1296c4c 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -826,6 +826,7 @@ class SettingsController extends Controller $setting->is_ad = $request->input('is_ad', '0'); $setting->ldap_tls = $request->input('ldap_tls', '0'); $setting->ldap_pw_sync = $request->input('ldap_pw_sync', '0'); + $setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url'); if ($setting->save()) { return redirect()->route('settings.index') diff --git a/app/Http/Controllers/StatuslabelsController.php b/app/Http/Controllers/StatuslabelsController.php index dc46f283b..0b075d6e7 100755 --- a/app/Http/Controllers/StatuslabelsController.php +++ b/app/Http/Controllers/StatuslabelsController.php @@ -200,17 +200,15 @@ class StatuslabelsController extends Controller { // Check if the Statuslabel exists if (is_null($statuslabel = Statuslabel::find($statuslabelId))) { - // Redirect to the blogs management page return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.not_found')); } - - if ($statuslabel->has_assets() == 0) { + // Check that there are no assets associated + if ($statuslabel->assets()->count() == 0) { $statuslabel->delete(); - // Redirect to the statuslabels management page return redirect()->route('statuslabels.index')->with('success', trans('admin/statuslabels/message.delete.success')); } - // Redirect to the asset management page + return redirect()->route('statuslabels.index')->with('error', trans('admin/statuslabels/message.assoc_assets')); } diff --git a/app/Http/Controllers/SuppliersController.php b/app/Http/Controllers/SuppliersController.php index 04657e06a..6e579180c 100755 --- a/app/Http/Controllers/SuppliersController.php +++ b/app/Http/Controllers/SuppliersController.php @@ -13,6 +13,7 @@ use Str; use View; use Auth; use Illuminate\Http\Request; +use App\Http\Requests\ImageUploadRequest; use Symfony\Component\HttpFoundation\JsonResponse; @@ -56,7 +57,7 @@ class SuppliersController extends Controller * @param Request $request * @return \Illuminate\Http\RedirectResponse */ - public function store(Request $request) + public function store(ImageUploadRequest $request) { // Create a new supplier $supplier = new Supplier; @@ -135,7 +136,7 @@ class SuppliersController extends Controller * @param int $supplierId * @return \Illuminate\Http\RedirectResponse */ - public function update($supplierId = null, Request $request) + public function update($supplierId = null, ImageUploadRequest $request) { // Check if the supplier exists if (is_null($supplier = Supplier::find($supplierId))) { @@ -158,18 +159,17 @@ class SuppliersController extends Controller $supplier->url = $supplier->addhttp(request('url')); $supplier->notes = request('notes'); + if (Input::file('image')) { $image = $request->file('image'); - $file_name = str_random(25).".".$image->getClientOriginalExtension(); + $file_name = 'suppliers-'.str_random(25).".".$image->getClientOriginalExtension(); $path = public_path('uploads/suppliers/'.$file_name); Image::make($image->getRealPath())->resize(300, null, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); })->save($path); $supplier->image = $file_name; - } - - if (request('image_delete') == 1 && $request->file('image') == "") { + } elseif (request('image_delete') == 1) { $supplier->image = null; } diff --git a/app/Http/Requests/AssetCheckoutRequest.php b/app/Http/Requests/AssetCheckoutRequest.php index 7c341b302..d0991b308 100644 --- a/app/Http/Requests/AssetCheckoutRequest.php +++ b/app/Http/Requests/AssetCheckoutRequest.php @@ -23,10 +23,13 @@ class AssetCheckoutRequest extends Request */ public function rules() { - return [ + $rules = [ "assigned_user" => 'required_without_all:assigned_asset,assigned_location', - "assigned_asset" => 'required_without_all:assigned_user,assigned_location', + "assigned_asset" => 'required_without_all:assigned_user,assigned_location|different:'.$this->id, "assigned_location" => 'required_without_all:assigned_user,assigned_asset', ]; + + + return $rules; } } diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index c593b7d20..b4143ec58 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -25,6 +25,7 @@ class AccessoriesTransformer 'name' => e($accessory->name), 'company' => ($accessory->company) ? ['id' => $accessory->company->id,'name'=> e($accessory->company->name)] : null, 'manufacturer' => ($accessory->manufacturer) ? ['id' => $accessory->manufacturer->id,'name'=> e($accessory->manufacturer->name)] : null, + 'supplier' => ($accessory->supplier) ? ['id' => $accessory->supplier->id,'name'=> e($accessory->supplier->name)] : null, 'model_number' => ($accessory->model_number) ? e($accessory->model_number) : null, 'category' => ($accessory->category) ? ['id' => $accessory->category->id,'name'=> e($accessory->category->name)] : null, 'location' => ($accessory->location) ? ['id' => $accessory->location->id,'name'=> e($accessory->location->name)] : null, @@ -35,6 +36,7 @@ class AccessoriesTransformer 'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null, 'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, 'remaining_qty' => $accessory->numRemaining(), + 'image' => ($accessory->image) ? url('/').'/uploads/accessories/'.e($accessory->image) : null, 'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'), diff --git a/app/Http/Transformers/CategoriesTransformer.php b/app/Http/Transformers/CategoriesTransformer.php index edb56c012..213793c90 100644 --- a/app/Http/Transformers/CategoriesTransformer.php +++ b/app/Http/Transformers/CategoriesTransformer.php @@ -39,7 +39,7 @@ class CategoriesTransformer $permissions_array['available_actions'] = [ 'update' => Gate::allows('update', Category::class) ? true : false, - 'delete' => Gate::allows('delete', Category::class) ? true : false, + 'delete' => (Gate::allows('delete', Category::class) && ($category->assets_count == 0) && ($category->accessories_count == 0) && ($category->consumables_count == 0) && ($category->components_count == 0)) ? true : false, ]; $array += $permissions_array; diff --git a/app/Http/Transformers/StatuslabelsTransformer.php b/app/Http/Transformers/StatuslabelsTransformer.php index 7eb3be4f4..e7b356deb 100644 --- a/app/Http/Transformers/StatuslabelsTransformer.php +++ b/app/Http/Transformers/StatuslabelsTransformer.php @@ -26,6 +26,7 @@ class StatuslabelsTransformer 'type' => $statuslabel->getStatuslabelType(), 'color' => ($statuslabel->color) ? e($statuslabel->color) : null, 'show_in_nav' => ($statuslabel->show_in_nav=='1') ? true : false, + 'assets_count' => (int) $statuslabel->assets_count, 'notes' => e($statuslabel->notes), 'created_at' => Helper::getFormattedDateObject($statuslabel->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($statuslabel->updated_at, 'datetime'), @@ -33,7 +34,7 @@ class StatuslabelsTransformer $permissions_array['available_actions'] = [ 'update' => Gate::allows('update', Statuslabel::class) ? true : false, - 'delete' => Gate::allows('delete', Statuslabel::class) ? true : false, + 'delete' => (Gate::allows('delete', Statuslabel::class) && ($statuslabel->assets_count == 0)) ? true : false, ]; $array += $permissions_array; diff --git a/app/Http/Transformers/SuppliersTransformer.php b/app/Http/Transformers/SuppliersTransformer.php index f588ae046..5802c87d8 100644 --- a/app/Http/Transformers/SuppliersTransformer.php +++ b/app/Http/Transformers/SuppliersTransformer.php @@ -36,8 +36,9 @@ class SuppliersTransformer 'email' => ($supplier->email) ? e($supplier->email) : null, 'contact' => ($supplier->contact) ? e($supplier->contact) : null, 'assets_count' => (int) $supplier->assets_count, + 'accessories_count' => (int) $supplier->accessories_count, 'licenses_count' => (int) $supplier->licenses_count, - 'image' => ($supplier->image) ? e($supplier->image) : null, + 'image' => ($supplier->image) ? url('/').'/uploads/suppliers/'.e($supplier->image) : null, 'notes' => ($supplier->notes) ? e($supplier->notes) : null, 'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'), @@ -46,7 +47,7 @@ class SuppliersTransformer $permissions_array['available_actions'] = [ 'update' => Gate::allows('update', Supplier::class) ? true : false, - 'delete' => Gate::allows('delete', Supplier::class) ? true : false, + 'delete' => (Gate::allows('delete', Supplier::class) && ($supplier->assets_count == 0) && ($supplier->licenses_count == 0) && ($supplier->accessories_count == 0)) ? true : false, ]; $array += $permissions_array; diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index d32a6e115..68e92e1ce 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -17,7 +17,7 @@ class Accessory extends SnipeModel use Loggable, Presentable; use SoftDeletes; - protected $dates = ['deleted_at', 'purchase_date']; + protected $dates = ['deleted_at']; protected $table = 'accessories'; protected $casts = [ 'requestable' => 'boolean' @@ -61,10 +61,19 @@ class Accessory extends SnipeModel 'purchase_date', 'model_number', 'manufacturer_id', + 'supplier_id', + 'image', 'qty', 'requestable' ]; + + public function supplier() + { + return $this->belongsTo('\App\Models\Supplier', 'supplier_id'); + } + + public function setRequestableAttribute($value) { if ($value == '') { diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 1404c4e1f..b0d032da9 100755 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -38,6 +38,7 @@ class Setting extends Model "pwd_secure_min" => "numeric|required|min:5", "audit_warning_days" => "numeric|nullable", "audit_interval" => "numeric|nullable", + "custom_forgot_pass_url" => "url|nullable", ]; protected $fillable = ['site_name','email_domain','email_format','username_format']; diff --git a/app/Models/Statuslabel.php b/app/Models/Statuslabel.php index 2bb92dec4..c650521ca 100755 --- a/app/Models/Statuslabel.php +++ b/app/Models/Statuslabel.php @@ -29,16 +29,6 @@ class Statuslabel extends SnipeModel protected $fillable = ['name', 'deployable', 'pending', 'archived']; - /** - * Show count of assets with status label - * - * @todo Remove this. It's dumb. - * @return \Illuminate\Support\Collection - */ - public function has_assets() - { - return $this->hasMany('\App\Models\Asset', 'status_id')->count(); - } /** * Get assets with associated status label @@ -64,6 +54,27 @@ class Statuslabel extends SnipeModel } } + public function scopePending() + { + return $this->where('pending', '=', 1) + ->where('archived', '=', 0) + ->where('deployable', '=', 0); + } + + public function scopeArchived() + { + return $this->where('pending', '=', 0) + ->where('archived', '=', 1) + ->where('deployable', '=', 0); + } + + public function scopeDeployable() + { + return $this->where('pending', '=', 0) + ->where('archived', '=', 0) + ->where('deployable', '=', 1); + } + public static function getStatuslabelTypesForDB($type) { diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index c4e1d926b..aae2047df 100755 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -68,6 +68,11 @@ class Supplier extends SnipeModel return $this->hasMany('\App\Models\Asset', 'supplier_id'); } + public function accessories() + { + return $this->hasMany('\App\Models\Accessory', 'supplier_id'); + } + public function asset_maintenances() { return $this->hasMany('\App\Models\AssetMaintenance', 'supplier_id'); diff --git a/app/Presenters/AccessoryPresenter.php b/app/Presenters/AccessoryPresenter.php index 5032fdb54..e38b134b8 100644 --- a/app/Presenters/AccessoryPresenter.php +++ b/app/Presenters/AccessoryPresenter.php @@ -31,6 +31,14 @@ class AccessoryPresenter extends Presenter "switchable" => true, "title" => trans('general.id'), "visible" => false + ],[ + "field" => "image", + "searchable" => false, + "sortable" => true, + "switchable" => true, + "title" => trans('admin/hardware/table.image'), + "visible" => true, + "formatter" => "imageFormatter" ], [ "field" => "company", "searchable" => true, @@ -63,6 +71,14 @@ class AccessoryPresenter extends Presenter "sortable" => true, "title" => trans('general.manufacturer'), "formatter" => "manufacturersLinkObjFormatter", + ], [ + "field" => "supplier", + "searchable" => true, + "sortable" => true, + "switchable" => true, + "title" => trans('general.supplier'), + "visible" => false, + "formatter" => "suppliersLinkObjFormatter" ], [ "field" => "location", "searchable" => true, diff --git a/app/Presenters/CategoryPresenter.php b/app/Presenters/CategoryPresenter.php index 4b545b82d..42303f8e4 100644 --- a/app/Presenters/CategoryPresenter.php +++ b/app/Presenters/CategoryPresenter.php @@ -41,25 +41,25 @@ class CategoryPresenter extends Presenter ], [ "field" => "assets_count", "searchable" => false, - "sortable" => false, + "sortable" => true, "title" => trans('general.assets'), "visible" => true ], [ "field" => "accessories_count", "searchable" => false, - "sortable" => false, + "sortable" => true, "title" => trans('general.accessories'), "visible" => true ], [ "field" => "consumables_count", "searchable" => false, - "sortable" => false, + "sortable" => true, "title" => trans('general.consumables'), "visible" => true ], [ "field" => "components_count", "searchable" => false, - "sortable" => false, + "sortable" => true, "title" => trans('general.components'), "visible" => true ], [ @@ -72,7 +72,7 @@ class CategoryPresenter extends Presenter ], [ "field" => "require_acceptance", "searchable" => false, - "sortable" => false, + "sortable" => true, "title" => trans('admin/categories/table.require_acceptance'), "visible" => true, "formatter" => 'trueFalseFormatter', diff --git a/database/factories/AccessoryFactory.php b/database/factories/AccessoryFactory.php index e96e487fc..352f0b154 100644 --- a/database/factories/AccessoryFactory.php +++ b/database/factories/AccessoryFactory.php @@ -24,7 +24,8 @@ $factory->state(App\Models\Accessory::class, 'apple-bt-keyboard', function ($fak 'category_id' => 8, 'manufacturer_id' => 1, 'qty' => 10, - 'min_amt' => 2 + 'min_amt' => 2, + 'supplier_id' => rand(1,5) ]; }); @@ -36,7 +37,8 @@ $factory->state(App\Models\Accessory::class, 'apple-usb-keyboard', function ($fa 'category_id' => 8, 'manufacturer_id' => 1, 'qty' => 15, - 'min_amt' => 2 + 'min_amt' => 2, + 'supplier_id' => rand(1,5) ]; }); @@ -48,7 +50,8 @@ $factory->state(App\Models\Accessory::class, 'apple-mouse', function ($faker) { 'category_id' => 9, 'manufacturer_id' => 1, 'qty' => 13, - 'min_amt' => 2 + 'min_amt' => 2, + 'supplier_id' => rand(1,5) ]; }); @@ -56,7 +59,7 @@ $factory->state(App\Models\Accessory::class, 'apple-mouse', function ($faker) { $factory->state(App\Models\Accessory::class, 'microsoft-mouse', function ($faker) { return [ - 'name' => 'Sculpt Comfort Mouse\'', + 'name' => 'Sculpt Comfort Mouse', 'category_id' => 9, 'manufacturer_id' => 2, 'qty' => 13, diff --git a/database/migrations/2017_10_19_120002_add_custom_forgot_password_url.php b/database/migrations/2017_10_19_120002_add_custom_forgot_password_url.php new file mode 100644 index 000000000..7a1776e25 --- /dev/null +++ b/database/migrations/2017_10_19_120002_add_custom_forgot_password_url.php @@ -0,0 +1,32 @@ +string('custom_forgot_pass_url')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('custom_forgot_pass_url'); + }); + } +} diff --git a/database/migrations/2017_10_19_130406_add_image_and_supplier_to_accessories.php b/database/migrations/2017_10_19_130406_add_image_and_supplier_to_accessories.php new file mode 100644 index 000000000..4ac9dfeb8 --- /dev/null +++ b/database/migrations/2017_10_19_130406_add_image_and_supplier_to_accessories.php @@ -0,0 +1,34 @@ +string('image')->nullable()->default(null); + $table->integer('supplier_id')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accessories', function (Blueprint $table) { + $table->dropColumn('image'); + $table->dropColumn('supplier_id'); + }); + } +} diff --git a/public/uploads/accessories/.gitignore b/public/uploads/accessories/.gitignore new file mode 100755 index 000000000..f935021a8 --- /dev/null +++ b/public/uploads/accessories/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/public/uploads/accessories/.gitkeep b/public/uploads/accessories/.gitkeep new file mode 100755 index 000000000..e69de29bb diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php index 1beed4ea5..3dd1efe70 100644 --- a/resources/lang/en/admin/settings/general.php +++ b/resources/lang/en/admin/settings/general.php @@ -23,6 +23,8 @@ return array( 'confirm_purge_help' => 'Enter the text "DELETE" in the box below to purge your deleted records. This action cannot be undone.', 'custom_css' => 'Custom CSS', 'custom_css_help' => 'Enter any custom CSS overrides you would like to use. Do not include the <style></style> tags.', + 'custom_forgot_pass_url' => 'Custom Password Reset URL', + 'custom_forgot_pass_url_help' => 'This replaces the built-in forgotten password URL on the login screen, useful to direct people to internal or hosted LDAP password reset functionality. It will effectively disable local user forgotten password functionality.', 'default_currency' => 'Default Currency', 'default_eula_text' => 'Default EULA', 'default_language' => 'Default Language', @@ -44,6 +46,8 @@ return array( 'ldap_enabled' => 'LDAP enabled', 'ldap_integration' => 'LDAP Integration', 'ldap_settings' => 'LDAP Settings', + 'ldap_login_test_help' => 'Enter a valid LDAP username and password to test whether your LDAP login is configured correctly.', + 'ldap_login_sync_help' => 'This only tests that LDAP can sync correctly. If your LDAP Authentication query is not correct, users may still not be able to login.', 'ldap_server' => 'LDAP Server', 'ldap_server_help' => 'This should start with ldap:// (for unencrypted or TLS) or ldaps:// (for SSL)', 'ldap_server_cert' => 'LDAP SSL certificate validation', diff --git a/resources/views/accessories/edit.blade.php b/resources/views/accessories/edit.blade.php index d851d2fab..4c361682a 100755 --- a/resources/views/accessories/edit.blade.php +++ b/resources/views/accessories/edit.blade.php @@ -12,6 +12,7 @@ @include ('partials.forms.edit.company') @include ('partials.forms.edit.name', ['translated_name' => trans('admin/accessories/general.accessory_name')]) @include ('partials.forms.edit.category') +@include ('partials.forms.edit.supplier') @include ('partials.forms.edit.manufacturer') @include ('partials.forms.edit.location') @include ('partials.forms.edit.model_number') @@ -21,4 +22,20 @@ @include ('partials.forms.edit.quantity') @include ('partials.forms.edit.minimum_quantity') + + +
+ {{ Form::label('image', trans('general.image_upload'), array('class' => 'col-md-3 control-label')) }} +
+ @if (config('app.lock_passwords')) +

{{ trans('general.lock_passwords') }}

+ @else + {{ Form::file('image') }} + {!! $errors->first('image', ':message') !!} + @endif +
+
+ + + @stop diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index e6d4316f4..114ea6b21 100755 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -63,7 +63,13 @@
- {{ trans('auth/general.forgot_password') }} + @if ($snipeSettings->custom_forgot_pass_url) + {{ trans('auth/general.forgot_password') }} + @else + {{ trans('auth/general.forgot_password') }} + @endif + +
diff --git a/resources/views/auth/passwords/email.blade.php b/resources/views/auth/passwords/email.blade.php index 853b6e010..d2e1cd344 100644 --- a/resources/views/auth/passwords/email.blade.php +++ b/resources/views/auth/passwords/email.blade.php @@ -3,6 +3,11 @@ {{-- Page content --}} @section('content') + + @if ($snipeSettings->custom_forgot_pass_url) + {{ trans('auth/general.forgot_password') }} + @else +
{!! csrf_field() !!}
@@ -50,5 +55,7 @@
+ + @endif @stop diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 209ec3bd5..cb2ab5cc4 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -198,12 +198,16 @@ $('.snipe-table').bootstrapTable({ + ' data-toggle="modal" ' + ' data-content="{{ trans('general.sure_to_delete') }} ' + row.name + '?" ' + ' data-title="{{ trans('general.delete') }}" onClick="return false;">' - + ''; + + ' '; + } else { + actions += ' '; } if ((row.available_actions) && (row.available_actions.restore === true)) { actions += ' '; } + + actions +=''; return actions; }; diff --git a/resources/views/settings/ldap.blade.php b/resources/views/settings/ldap.blade.php index 705fdcafc..bc5724e82 100644 --- a/resources/views/settings/ldap.blade.php +++ b/resources/views/settings/ldap.blade.php @@ -334,9 +334,9 @@ @if ($setting->ldap_enabled) -
+
- Test LDAP Sync + {{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
Test LDAP @@ -347,14 +347,14 @@
-

This only tests that LDAP can sync correctly. If your LDAP Authentication query is not correct, users may still not be able to login.

+

{{ trans('admin/settings/general.ldap_login_sync_help') }}

-
+
- Test LDAP Login + {{ Form::label('test_ldap_login', 'Test LDAP Login') }}
@@ -377,12 +377,29 @@
-

Enter a valid LDAP username and password to test whether your LDAP login is configured correctly.

+

{{ trans('admin/settings/general.ldap_login_test_help') }}

@endif + +
+
+ {{ Form::label('custom_forgot_pass_url', trans('admin/settings/general.custom_forgot_pass_url')) }} +
+
+ @if (config('app.lock_passwords')===true) + {{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'https://my.ldapserver-forgotpass.com')) }} + @else + {{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control','placeholder' => 'https://my.ldapserver-forgotpass.com')) }} + @endif +

{{ trans('admin/settings/general.custom_forgot_pass_url_help') }}

+ {!! $errors->first('custom_forgot_pass_url', ':message') !!} +
+
+ +
@@ -73,12 +92,30 @@ } } - function undeployableFormatter(value, row) { - if ((value) && (value!='deployable')) { - return '' + value + ' '; - } else { - return '' + value + ' '; + function statusLabelTypeFormatter (row, value) { + switch (value.type) { + case 'deployed': + text_color = 'blue'; + icon_style = 'fa-circle'; + break; + case 'deployable': + text_color = 'green'; + icon_style = 'fa-circle'; + break; + case 'pending': + text_color = 'orange'; + icon_style = 'fa-circle'; + break; + default: + text_color = 'red'; + icon_style = 'fa-times'; } + + var typename_lower = value.type; + var typename = typename_lower.charAt(0).toUpperCase() + typename_lower.slice(1); + return ' ' + typename; + + } @stop diff --git a/resources/views/suppliers/index.blade.php b/resources/views/suppliers/index.blade.php index ff53eeb1f..7a9b3f0e3 100755 --- a/resources/views/suppliers/index.blade.php +++ b/resources/views/suppliers/index.blade.php @@ -30,14 +30,16 @@ {{ trans('admin/suppliers/table.id') }} + Image {{ trans('admin/suppliers/table.name') }} {{ trans('admin/suppliers/table.address') }} {{ trans('admin/suppliers/table.contact') }} {{ trans('admin/suppliers/table.email') }} {{ trans('admin/suppliers/table.phone') }} {{ trans('admin/suppliers/table.fax') }} - {{ trans('admin/suppliers/table.assets') }} - {{ trans('admin/suppliers/table.licenses') }} + {{ trans('admin/suppliers/table.assets') }} + {{ trans('general.accessories') }} + {{ trans('admin/suppliers/table.licenses') }} {{ trans('table.actions') }} diff --git a/resources/views/suppliers/view.blade.php b/resources/views/suppliers/view.blade.php index 068e0d548..4b1c7120d 100755 --- a/resources/views/suppliers/view.blade.php +++ b/resources/views/suppliers/view.blade.php @@ -137,6 +137,40 @@
+ +
+
+
+

Accessories

+
+
+
+
+ + + + + + + + + + + +
NameModel NumberPurchase_costActions
+
+
+
+ +
@if ($supplier->id) @@ -239,3 +273,8 @@
@stop +@section('moar_scripts') + @include ('partials.bootstrap-table', [ + 'showFooter' => true, + ]) +@stop