Merge branch 'develop'

This commit is contained in:
snipe 2017-10-25 18:17:38 -07:00
commit 109ea82cb9
47 changed files with 316 additions and 255 deletions

View file

@ -67,7 +67,7 @@ class Handler extends ExceptionHandler
} }
if ($e instanceof \Illuminate\Validation\ValidationException) { if ($e instanceof \Illuminate\Validation\ValidationException) {
return response()->json(Helper::formatStandardApiResponse('error', $e->response['messages'], $e->getMessage(), 400)); return response()->json(Helper::formatStandardApiResponse('error', null, $e->response['messages'], 400));
} }
if ($this->isHttpException($e)) { if ($this->isHttpException($e)) {

View file

@ -10,6 +10,7 @@ use App\Models\User;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Http\Requests\SaveUserRequest; use App\Http\Requests\SaveUserRequest;
use App\Models\Asset; use App\Models\Asset;
use App\Http\Transformers\AssetsTransformer;
class UsersController extends Controller class UsersController extends Controller
{ {
@ -43,7 +44,9 @@ class UsersController extends Controller
'users.last_login', 'users.last_login',
'users.deleted_at', 'users.deleted_at',
'users.department_id', 'users.department_id',
'users.activated' 'users.activated',
'users.avatar',
])->with('manager', 'groups', 'userloc', 'company', 'department','throttle','assets','licenses','accessories','consumables') ])->with('manager', 'groups', 'userloc', 'company', 'department','throttle','assets','licenses','accessories','consumables')
->withCount('assets','licenses','accessories','consumables'); ->withCount('assets','licenses','accessories','consumables');
$users = Company::scopeCompanyables($users); $users = Company::scopeCompanyables($users);
@ -107,6 +110,78 @@ class UsersController extends Controller
} }
/**
* Display a listing of the resource.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
*
* @return \Illuminate\Http\Response
*/
public function selectlist(Request $request)
{
$this->authorize('view', User::class);
$users = User::select(
[
'users.id',
'users.employee_num',
'users.first_name',
'users.last_name',
'users.gravatar',
'users.avatar',
'users.email',
]
);
$users = Company::scopeCompanyables($users);
if ($request->has('search')) {
$users = $users->where('first_name', '=', '%'.$request->get('search').'%')
->orWhere('last_name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
->orWhere('employee_num', '=', '%'.$request->get('search').'%');
}
$users = $users->orderBy('last_name', 'asc');
$users = $users->paginate(50);
$users_array = [];
foreach ($users as $user) {
$name_str = '';
if ($user->last_name!='') {
$name_str .= e($user->last_name).', ';
}
$name_str .= e($user->first_name);
if ($user->employee_num!='') {
$name_str .= ' (#'.e($user->employee_num).')';
}
$users_array[] =
[
'id' => $user->id,
'text' => $name_str,
'image' => ($user->present()->gravatar) ? $user->present()->gravatar : null,
];
}
$results = [
'items' => $users_array,
'pagination' =>
[
'more' => ($users->currentPage() >= $users->lastPage()) ? false : true,
'per_page' => $users->perPage()
],
'total_count' => $users->total(),
'page' => $users->currentPage(),
'page_count' => $users->lastPage()
];
return $results;
}
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
@ -123,7 +198,7 @@ class UsersController extends Controller
$user->password = bcrypt($request->input('password')); $user->password = bcrypt($request->input('password'));
if ($user->save()) { if ($user->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.create.success'))); return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
} }
return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors())); return response()->json(Helper::formatStandardApiResponse('error', null, $user->getErrors()));
} }
@ -207,6 +282,7 @@ class UsersController extends Controller
{ {
$this->authorize('view', User::class); $this->authorize('view', User::class);
$assets = Asset::where('assigned_to', '=', $id)->with('model')->get(); $assets = Asset::where('assigned_to', '=', $id)->with('model')->get();
return response()->json($assets); return (new AssetsTransformer)->transformAssets($assets, $assets->count());
// return response()->json($assets);
} }
} }

View file

@ -30,12 +30,13 @@ class SaveUserRequest extends Request
switch($this->method()) switch($this->method())
{ {
// Brand new asset // Brand new user
case 'POST': case 'POST':
{ {
$rules['first_name'] = 'required|string|min:1'; $rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1'; $rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('store'); $rules['password'] = Setting::passwordComplexityRulesSaving('store');
break;
} }
// Save all fields // Save all fields
@ -43,11 +44,13 @@ class SaveUserRequest extends Request
$rules['first_name'] = 'required|string|min:1'; $rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1'; $rules['username'] = 'required_unless:ldap_import,1|string|min:1';
$rules['password'] = Setting::passwordComplexityRulesSaving('update'); $rules['password'] = Setting::passwordComplexityRulesSaving('update');
break;
// Save only what's passed // Save only what's passed
case 'PATCH': case 'PATCH':
{ {
$rules['password'] = Setting::passwordComplexityRulesSaving('update'); $rules['password'] = Setting::passwordComplexityRulesSaving('update');
break;
} }
default:break; default:break;

View file

@ -23,6 +23,7 @@ class UsersTransformer
{ {
$array = [ $array = [
'id' => (int) $user->id, 'id' => (int) $user->id,
'avatar' => e($user->present()->gravatar),
'name' => e($user->first_name).' '.($user->last_name), 'name' => e($user->first_name).' '.($user->last_name),
'firstname' => e($user->first_name), 'firstname' => e($user->first_name),
'lastname' => e($user->last_name), 'lastname' => e($user->last_name),

View file

@ -30,6 +30,8 @@ class AssetImporter extends ItemImporter
if ($customFieldValue) { if ($customFieldValue) {
$this->item['custom_fields'][$customField->db_column_name()] = $customFieldValue; $this->item['custom_fields'][$customField->db_column_name()] = $customFieldValue;
$this->log('Custom Field '. $customField->name.': '.$customFieldValue); $this->log('Custom Field '. $customField->name.': '.$customFieldValue);
} else {
$this->item['custom_fields'][$customField->db_column_name()] = '';
} }
} }
} }

View file

@ -184,7 +184,7 @@ class Asset extends Depreciable
} else { } else {
$user_name = "Unassigned"; $user_name = "Unassigned";
} }
return $this->asset_tag . ' - ' . $this->name . ' (' . $user_name . ') ' . $this->model->name; return $this->asset_tag . ' - ' . $this->name . ' (' . $user_name . ') ' . ($this->model) ? $this->model->name: '';
} }
public function validationRules($id = '0') public function validationRules($id = '0')

View file

@ -59,7 +59,18 @@ class Location extends SnipeModel
public function locationAssets() public function locationAssets()
{ {
return $this->hasMany('\App\Models\Asset', 'rtd_location_id')->orHas('assignedAssets'); /* This used to have an ...->orHas() clause that referred to
assignedAssets, and that was probably incorrect, as well as
definitely was setting fire to the query-planner. So don't do that.
It is arguable that we should have a '...->whereNull('assigned_to')
bit in there, but that isn't always correct either (in the case
where a user has no location, for example).
In all likelyhood, we need to denorm an "effective_location" column
into Assets to make this slightly less miserable.
*/
return $this->hasMany('\App\Models\Asset', 'rtd_location_id');
} }
public function parent() public function parent()

View file

@ -34,6 +34,15 @@ class UserPresenter extends Presenter
"title" => trans('general.id'), "title" => trans('general.id'),
"visible" => false "visible" => false
], ],
[
"field" => "avatar",
"searchable" => false,
"sortable" => false,
"switchable" => true,
"title" => 'Avatar',
"visible" => false,
"formatter" => "imageFormatter"
],
[ [
"field" => "company", "field" => "company",
"searchable" => true, "searchable" => true,
@ -278,7 +287,8 @@ class UserPresenter extends Presenter
return "//gravatar.com/avatar/".$gravatar; return "//gravatar.com/avatar/".$gravatar;
} }
return false; // Set a fun, gender-neutral default icon
return url('/').'/img/default-sm.png';
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,23 +0,0 @@
{
"/build/vue.js": "/build/vue.js",
"/mix.js": "/mix.js",
"/build/app.css": "/build/app.535d8af1016a2377e449920c617f0197.css",
"/build/AdminLTE.css": "/build/AdminLTE.3d8a2b2e33baa060b1b324363ad5e1c2.css",
"/build/overrides.css": "/build/overrides.617623c6a96be3e0cbd11c5d4039ec10.css",
"/css/all.css": "/css/all.css",
"/js/all.js": "/js/all.js",
"/css/app.css": "/css/app.css",
"/css/dist/all.css": "/css/dist/all.css",
"/js/dist/all.js": "/js/dist/all.js",
"/css/AdminLTE.css": "/css/AdminLTE.css",
"/css/overrides.css": "/css/overrides.css",
"/css/skin-blue.css": "/css/skin-blue.css",
"/vue.js": "/vue.js",
"/vue.js.map": "/vue.js.map",
"/mix.js.map": "/mix.js.map",
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map",
"/css/app.css.map": "/css/app.css.map",
"/css/overrides.css.map": "/css/overrides.css.map",
"public/css/dist/all.css": "public/css/dist/all.css",
"public/js/dist/all.js": "public/js/dist/all.js"
}

View file

@ -1,2 +0,0 @@
!function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=62)}({6:function(n,t){},62:function(n,t,r){r(9),r(7),r(8),n.exports=r(6)},7:function(n,t){},8:function(n,t){},9:function(n,t){}});
//# sourceMappingURL=mix.js.map

View file

@ -1 +0,0 @@
{"version":3,"file":"mix.js","sources":["webpack:///mix.js"],"sourcesContent":["!function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,\"a\",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p=\"\",t(t.s=62)}({6:function(n,t){},62:function(n,t,r){r(9),r(7),r(8),n.exports=r(6)},7:function(n,t){},8:function(n,t){},9:function(n,t){}});\n\n\n// WEBPACK FOOTER //\n// mix.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
<?php <?php
return array ( return array (
'app_version' => 'v4.0.15', 'app_version' => 'v4.0.15',
'build_version' => '339', 'build_version' => '362',
'hash_version' => 'g17b2719', 'hash_version' => 'ga305b1e',
'full_hash' => 'v4.0.15-339-g17b2719', 'full_hash' => 'v4.0.15-362-ga305b1e',
); );

View file

@ -17,40 +17,6 @@ class UserSeeder extends Seeder
factory(User::class, 1)->states('snipe-admin')->create(); factory(User::class, 1)->states('snipe-admin')->create();
factory(User::class, 3)->states('superuser')->create(); factory(User::class, 3)->states('superuser')->create();
factory(User::class, 3)->states('admin')->create(); factory(User::class, 3)->states('admin')->create();
factory(User::class, 1)->states('view-assets')->create(); factory(User::class, 50)->states('view-assets')->create();
// factory(User::class, 1)->states('create-assets')->create();
// factory(User::class, 1)->states('edit-assets')->create();
// factory(User::class, 1)->states('delete-assets')->create();
// factory(User::class, 1)->states('checkin-assets')->create();
// factory(User::class, 1)->states('checkout-assets')->create();
// factory(User::class, 1)->states('view-requestable-assets')->create();
// factory(User::class, 1)->states('view-accessories')->create();
// factory(User::class, 1)->states('create-accessories')->create();
// factory(User::class, 1)->states('view-accessories')->create();
// factory(User::class, 1)->states('delete-accessories')->create();
// factory(User::class, 1)->states('edit-accessories')->create();
// factory(User::class, 1)->states('checkout-accessories')->create();
// factory(User::class, 1)->states('checkin-accessories')->create();
// factory(User::class, 1)->states('view-consumables')->create();
// factory(User::class, 1)->states('create-consumables')->create();
// factory(User::class, 1)->states('edit-consumables')->create();
// factory(User::class, 1)->states('delete-consumables')->create();
// factory(User::class, 1)->states('checkout-consumables')->create();
// factory(User::class, 1)->states('view-licenses')->create();
// factory(User::class, 1)->states('edit-licenses')->create();
// factory(User::class, 1)->states('delete-licenses')->create();
// factory(User::class, 1)->states('create-licenses')->create();
// factory(User::class, 1)->states('checkout-licenses')->create();
// factory(User::class, 1)->states('view-keys-licenses')->create();
// factory(User::class, 1)->states('view-components')->create();
// factory(User::class, 1)->states('edit-components')->create();
// factory(User::class, 1)->states('create-components')->create();
// factory(User::class, 1)->states('delete-components')->create();
// factory(User::class, 1)->states('checkout-components')->create();
// factory(User::class, 1)->states('checkin-components')->create();
// factory(User::class, 1)->states('view-users')->create();
// factory(User::class, 1)->states('edit-users')->create();
// factory(User::class, 1)->states('delete-users')->create();
// factory(User::class, 1)->states('create-users')->create();
} }
} }

2
public/build/vue.js Normal file

File diff suppressed because one or more lines are too long

1
public/build/vue.js.map Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

BIN
public/css/build/all.css Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/img/default-sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
public/img/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/js/build/all.js Normal file

Binary file not shown.

BIN
public/js/build/vue.js Normal file

Binary file not shown.

BIN
public/js/build/vue.js.map Normal file

Binary file not shown.

BIN
public/js/dist/all.js vendored

Binary file not shown.

View file

@ -1,15 +1,10 @@
{ {
"/build/vue.js": "/build/vue.js", "/js/build/vue.js": "/js/build/vue.js?id=e9504cad01a748f9b0fa",
"/mix.js": "/mix.js", "/css/AdminLTE.css": "/css/AdminLTE.css?id=889dc040f2ddfca6efde",
"/build/app.css": "/build/app.535d8af1016a2377e449920c617f0197.css", "/css/app.css": "/css/app.css?id=3a1e8c168fa8714043a6",
"/build/AdminLTE.css": "/build/AdminLTE.3d8a2b2e33baa060b1b324363ad5e1c2.css", "/css/overrides.css": "/css/overrides.css?id=3911514a8a64a4247483",
"/build/overrides.css": "/build/overrides.617623c6a96be3e0cbd11c5d4039ec10.css", "/css/dist/all.css": "/css/dist/all.css?id=f2d4896e67e878a47434",
"/css/all.css": "/css/all.css", "/js/dist/all.js": "/js/dist/all.js?id=15363bc14ab0694d1275",
"/js/all.js": "/js/all.js", "/css/build/all.css": "/css/build/all.css?id=f2d4896e67e878a47434",
"/css/app.css": "/css/app.css", "/js/build/all.js": "/js/build/all.js?id=15363bc14ab0694d1275"
"/css/dist/all.css": "/css/dist/all.css",
"/js/dist/all.js": "/js/dist/all.js",
"/css/AdminLTE.css": "/css/AdminLTE.css",
"/css/overrides.css": "/css/overrides.css",
"/css/skin-blue.css": "/css/skin-blue.css"
} }

File diff suppressed because one or more lines are too long

View file

@ -58,8 +58,8 @@ tr {
</td> </td>
<td> <td>
<button type="button" class="btn btn-default" @click="processDetail = false">Cancel</button> <button type="button" class="btn btn-sm btn-default" @click="processDetail = false">Cancel</button>
<button type="submit" class="btn btn-primary" @click="postSave">Import</button> <button type="submit" class="btn btn-sm btn-primary" @click="postSave">Import</button>
<div class="alert alert-success col-md-5 col-md-offset-1" style="text-align:left" v-if="statusText">{{ this.statusText }}</div> <div class="alert alert-success col-md-5 col-md-offset-1" style="text-align:left" v-if="statusText">{{ this.statusText }}</div>
</td> </td>
</tr> </tr>

View file

@ -1,12 +1,4 @@
<style scoped>
td {
font-size: 13px;
}
th {
font-size: 13px;
}
</style>
<script> <script>
require('blueimp-file-upload'); require('blueimp-file-upload');

View file

@ -23,7 +23,7 @@
*/ */
$(function () { $(function () {
console.warn("Loading up Modal functionality.");
//handle modal-add-interstitial calls //handle modal-add-interstitial calls
var model, select; var model, select;
@ -45,7 +45,7 @@
$('#createModal').on('click','#modal-save', function () { $('#createModal').on('click','#modal-save', function () {
var data = {}; var data = {};
console.warn("We are about to SAVE!!! for model: "+model+" and select ID: "+select); //console.warn("We are about to SAVE!!! for model: "+model+" and select ID: "+select);
$('.modal-body input:visible').each(function (index, elem) { $('.modal-body input:visible').each(function (index, elem) {
var bits = elem.id.split("-"); var bits = elem.id.split("-");
if (bits[0] === "modal") { if (bits[0] === "modal") {
@ -59,11 +59,10 @@
}); });
data._token = Laravel.csrfToken; data._token = Laravel.csrfToken;
//console.log(data);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: "../api/v1/" + model + "s", url: baseUrl+ "/api/v1/" + model + "s",
headers: { headers: {
"X-Requested-With": 'XMLHttpRequest', "X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
@ -71,13 +70,15 @@
data: data, data: data,
success: function (result) { success: function (result) {
// {"status":"error","messages":{"name":["The name field is required."]}} console.dir(result);
if(result.status == "error") { if(result.status == "error") {
var error_message=""; var error_message="";
for(var field in result.messages) { for(var field in result.messages) {
error_message+="Problem(s) with field '"+field+"': "+result.messages[field].join(", "); error_message += "<li>Problem(s) with field <i><strong>" + field + "</strong></i>: " + result.messages[field];
console.dir(result.messages);
console.log('error_messages are: ' + error_message);
} }
//window.alert("Error adding "+model+": "+error_message);
$('#modal_error_msg').html(error_message).show(); $('#modal_error_msg').html(error_message).show();
return false; return false;
} }
@ -95,11 +96,9 @@
// this code adds the newly created object to that select // this code adds the newly created object to that select
var selector = document.getElementById(select); var selector = document.getElementById(select);
if(!selector) { if(!selector) {
// console.error("Could not find original <select> element with an id of: "+select);
return false; return false;
} }
//console.log(document.getElementById(select));
// console.dir(selector);
selector.options[selector.length] = new Option(name, id); selector.options[selector.length] = new Option(name, id);
selector.selectedIndex = selector.length - 1; selector.selectedIndex = selector.length - 1;
$(selector).trigger("change"); $(selector).trigger("change");
@ -109,10 +108,8 @@
}, },
error: function (result) { error: function (result) {
// console.log('Error: ' + result.responseJSON.error.message );
msg = result.responseJSON.messages || result.responseJSON.error; msg = result.responseJSON.messages || result.responseJSON.error;
$('#modal_error_msg').html("Server Error: "+msg).show(); $('#modal_error_msg').html("Server Error: "+msg).show();
//window.alert("Unable to add new " + model + " - error: " + msg);
} }

View file

@ -80,10 +80,7 @@ a.accordion-header {
.main-header { .main-header {
max-height: 150px; max-height: 150px;
} }
.navbar-brand-img {
float: left;
padding: 5px 5px 5px 0;
}
.navbar-nav>.user-menu>.dropdown-menu { .navbar-nav>.user-menu>.dropdown-menu {
width: inherit; width: inherit;
@ -311,3 +308,10 @@ body {
visibility: visible !important; visibility: visible !important;
} }
} }
img.navbar-brand-img, .navbar-brand>img {
float: left;
padding: 5px 5px 5px 0;
max-height: 50px;
}

View file

@ -27,7 +27,7 @@
<div class="box-body"> <div class="box-body">
{{csrf_field()}} {{csrf_field()}}
@if ($asset->model->name) @if ($asset->model->name)
<!-- Asset name --> <!-- Model name -->
<div class="form-group {{ $errors->has('name') ? 'error' : '' }}"> <div class="form-group {{ $errors->has('name') ? 'error' : '' }}">
{{ Form::label('name', trans('admin/hardware/form.model'), array('class' => 'col-md-3 control-label')) }} {{ Form::label('name', trans('admin/hardware/form.model'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-8"> <div class="col-md-8">
@ -45,14 +45,23 @@
</div> </div>
</div> </div>
<!-- User -->
<div id="assigned_user" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}"> <div id="assigned_user" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_user', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }} {{ Form::label('assigned_user', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
{{ Form::select('assigned_user', $users_list , Input::old('assigned_user', $asset->assigned_type == 'App\Models\User' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_user_select', 'style'=>'width:100%')) }} <div class="col-md-7 required">
<select class="js-data-user-ajax" name="assigned_user" style="width: 350px;">
<option value="">{{ trans('general.select_user') }}</option>
</select>
</div>
{!! $errors->first('assigned_user', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!} {!! $errors->first('assigned_user', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
<div class="col-md-1 col-sm-1 text-left"> <div class="col-md-1 col-sm-1 text-left">
@can('create', \App\Models\User::class) @can('create', \App\Models\User::class)
<a href='{{ route('modal.user') }}' data-toggle="modal" data-target="#createModal" data-dependency="user" data-select='assigned_user_select' class="btn btn-sm btn-default">New</a> <a href='{{ route('modal.user') }}' data-toggle="modal" data-target="#createModal" data-dependency="user" data-select='assigned_user_select' class="btn btn-sm btn-default">New</a>
@ -166,7 +175,7 @@ $(function() {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: '{{url('/') }}/api/v1/users/' + userid + '/assets', url: '{{ url('/') }}/api/v1/users/' + userid + '/assets',
headers: { headers: {
"X-Requested-With": 'XMLHttpRequest', "X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
@ -176,20 +185,45 @@ $(function() {
success: function (data) { success: function (data) {
$('#current_assets_box').fadeIn(); $('#current_assets_box').fadeIn();
var table_html = '<div class="row"><div class="col-md-12"><table class="table table-striped"><thead><tr><td>{{ trans('admin/hardware/form.name') }}</td><td>{{ trans('admin/hardware/form.tag') }}</td></tr></thead><tbody>'; var table_html = '<div class="row">';
table_html += '<div class="col-md-12">';
table_html += '<table class="table table-striped">';
table_html += '<thead><tr>';
table_html += '<th></th>';
table_html += '<th>{{ trans('admin/hardware/form.name') }}</th>';
table_html += '<th>{{ trans('admin/hardware/form.tag') }}</th>';
table_html += '<th>{{ trans('admin/hardware/form.serial') }}</th>';
table_html += '</tr></thead><tbody>';
$('#current_assets_content').append(''); $('#current_assets_content').append('');
for (var i in data) { if (data.rows.length > 0) {
var asset = data[i];
table_html += '<tr><td class="col-md-8"><a href="{{ url('/') }}/hardware/' + asset.id + '">' + asset.name;
if (asset.model.name!='') {
table_html += " (" + asset.model.name + ")";
for (var i in data.rows) {
var asset = data.rows[i];
table_html += '<tr>';
if (asset.image != null) {
table_html += '<td class="col-md-1"><a href="' + asset.image + '" data-toggle="lightbox" data-type="image"><img src="' + asset.image + '" style="max-height: {{ $snipeSettings->thumbnail_max_h }}px; width: auto;"></a></td>';
} else {
table_html += "<td></td> ";
}
table_html += '<td><a href="{{ url('/') }}/hardware/' + asset.id + '">';
if ((asset.name == '') && (asset.name != null)) {
table_html += " " + asset.model.name;
} else {
table_html += asset.name;
table_html += " (" + asset.model.name + ")";
}
table_html += '</a></td>';
table_html += '<td class="col-md-4">' + asset.asset_tag + '</td>';
table_html += '<td class="col-md-4">' + asset.serial + '</td>';
table_html += "</tr>";
} }
table_html += "</a></td><td class=\"col-md-4\">" + asset.asset_tag + "</td></tr>"; } else {
table_html += '<tr><td colspan="4">No assets checked out to '+ $('.js-data-user-ajax').find('option:selected').text() + ' yet!</td></tr>';
} }
$('#current_assets_content').html(table_html + '</tbody></table></div></div>'); $('#current_assets_content').html(table_html + '</tbody></table></div></div>');
}, },

View file

@ -1,12 +1,5 @@
@extends('layouts/default') @extends('layouts/default')
<link rel="stylesheet" type="text/css" href="{{ asset('css/lib/jquery.fileupload.css') }}">
{{-- Hide importer until vue has rendered it, if we continue using vue for other things we should move this higher in the style --}}
<style>
[v-cloak] {
display:none;
}
</style>
{{-- Page title --}} {{-- Page title --}}
@section('title') @section('title')
{{ trans('general.import') }} {{ trans('general.import') }}
@ -15,11 +8,22 @@
{{-- Page content --}} {{-- Page content --}}
@section('content') @section('content')
<link rel="stylesheet" type="text/css" href="{{ asset('css/lib/jquery.fileupload.css') }}">
{{-- Hide importer until vue has rendered it, if we continue using vue for other things we should move this higher in the style --}}
<style>
[v-cloak] {
display:none;
}
</style>
<div id="app"> <div id="app">
<importer inline-template v-cloak> <importer inline-template v-cloak>
<div class="row"> <div class="row">
<alert v-show="alert.visible" :alert-type="alert.type" v-on:hide="alert.visible = false">@{{ alert.message }}</alert> <alert v-show="alert.visible" :alert-type="alert.type" v-on:hide="alert.visible = false">@{{ alert.message }}</alert>
<errors :errors="importErrors"></errors> <errors :errors="importErrors"></errors>
<div class="col-md-12"> <div class="col-md-12">
<div class="box"> <div class="box">
<div class="box-body"> <div class="box-body">
@ -59,7 +63,7 @@
<td>@{{ currentFile.filesize }}</td> <td>@{{ currentFile.filesize }}</td>
<td> <td>
<button class="btn btn-sm btn-info" @click="toggleEvent(currentFile.id)">Process</button> <button class="btn btn-sm btn-info" @click="toggleEvent(currentFile.id)">Process</button>
<button class="btn btn-danger" @click="deleteFile(currentFile)"><i class="fa fa-trash icon-white"></i></button> <button class="btn btn-sm btn-danger" @click="deleteFile(currentFile)"><i class="fa fa-trash icon-white"></i></button>
</td> </td>
</tr> </tr>
<import-file <import-file

View file

@ -73,7 +73,7 @@
} }
}; };
</script> </script>
<!-- Add laravel route sinto javascript Primarily useful for vue.--> <!-- Add laravel routes into javascript Primarily useful for vue.-->
@routes @routes
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
@ -108,7 +108,7 @@
@if ($snipeSettings->brand == '3') @if ($snipeSettings->brand == '3')
<a class="logo navbar-brand no-hover" href="{{ url('/') }}"> <a class="logo navbar-brand no-hover" href="{{ url('/') }}">
@if ($snipeSettings->logo!='') @if ($snipeSettings->logo!='')
<img class="navbar-brand-img" style="max-height: 50px;" src="{{ url('/') }}/uploads/{{ $snipeSettings->logo }}"> <img class="navbar-brand-img" src="{{ url('/') }}/uploads/{{ $snipeSettings->logo }}">
@endif @endif
{{ $snipeSettings->site_name }} {{ $snipeSettings->site_name }}
</a> </a>
@ -663,7 +663,63 @@
var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value var datepicker = $.fn.datepicker.noConflict(); // return $.fn.datepicker to previously assigned value
$.fn.bootstrapDP = datepicker; $.fn.bootstrapDP = datepicker;
$('.datepicker').datepicker(); $('.datepicker').datepicker();
}) });
// Crazy select2 rich dropdowns with images!
$(".js-data-user-ajax").select2({
ajax: {
url: '{{ route('api.users.selectlist') }}',
dataType: 'json',
delay: 250,
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: function (params) {
var data = {
search: params.term,
page: params.page || 1
};
return data;
},
processResults: function (data, params) {
params.page = params.page || 1;
var answer = {
results: data.items,
pagination: {
more: "true" //(params.page < data.page_count)
}
};
return answer;
},
cache: true
},
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
templateResult: formatUserlist,
templateSelection: formatUserSelection
});
function formatUserlist (userlist) {
var loading_markup = '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> Loading...';
if (userlist.loading) {
return loading_markup;
}
var markup = "<div class='clearfix'>" ;
markup +="<div class='pull-left' style='padding-right: 10px;'>";
markup += "<img src='" + userlist.image + "' style='max-height: 20px'></div>";
markup += "<div>" + userlist.text + "</div>";
markup += "</div>";
return markup;
}
function formatUserSelection (userlist) {
return userlist.text;
}
</script> </script>

View file

@ -27,7 +27,7 @@
<div class="dynamic-form-row"> <div class="dynamic-form-row">
<div class="col-md-4 col-xs-12"><label for="modal-modelno">{{ trans('general.model_no') }}:</label></div> <div class="col-md-4 col-xs-12"><label for="modal-modelno">{{ trans('general.model_no') }}:</label></div>
<div class="col-md-8 col-xs-12"><input type='text' id='modal-modelno' class="form-control"></div> <div class="col-md-8 col-xs-12"><input type='text' id='modal-model_number' class="form-control"></div>
</div> </div>
<div class="dynamic-form-row"> <div class="dynamic-form-row">

View file

@ -229,7 +229,7 @@
<form action="{{ route('users/bulkedit') }}" method="POST"> <form action="{{ route('users/bulkedit') }}" method="POST">
<!-- CSRF Token --> <!-- CSRF Token -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" /> <input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input type="hidden" name="edit_user[{{ $user->id }}]" value="{{ $user->id }}" /> <input type="hidden" name="ids[{{ $user->id }}]" value="{{ $user->id }}" />
<button style="width: 100%;" class="btn btn-sm btn-danger hidden-print">{{ trans('button.checkin_and_delete') }}</button> <button style="width: 100%;" class="btn btn-sm btn-danger hidden-print">{{ trans('button.checkin_and_delete') }}</button>
</form> </form>
</div> </div>

View file

@ -532,20 +532,7 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
/*--- Users API ---*/ /*--- Users API ---*/
Route::resource('users', 'UsersController',
[
'names' =>
[
'index' => 'api.users.index',
'show' => 'api.users.show',
'store' => 'api.users.store',
'update' => 'api.users.update',
'destroy' => 'api.users.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['user' => 'user_id']
]
); // Users resource
Route::group([ 'prefix' => 'users' ], function () { Route::group([ 'prefix' => 'users' ], function () {
@ -563,6 +550,13 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
] ]
); );
Route::get('selectlist',
[
'as' => 'api.users.selectlist',
'uses' => 'UsersController@selectList'
]
);
Route::get('{user}/assets', Route::get('{user}/assets',
[ [
'as' => 'api.users.assetlist', 'as' => 'api.users.assetlist',
@ -578,6 +572,21 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
); );
}); // Users group }); // Users group
Route::resource('users', 'UsersController',
[
'names' =>
[
'index' => 'api.users.index',
'show' => 'api.users.show',
'store' => 'api.users.store',
'update' => 'api.users.update',
'destroy' => 'api.users.destroy'
],
'except' => ['create', 'edit'],
'parameters' => ['user' => 'user_id']
]
); // Users resource
Route::get( Route::get(
'reports/activity', 'reports/activity',

View file

@ -1,8 +1,6 @@
const { mix } = require('laravel-mix'); const { mix } = require('laravel-mix');
mix.setPublicPath('build'); //this throws everything to root dir 'build'
// This generates a file called app.css, which we use // This generates a file called app.css, which we use
// later on to build all.css // later on to build all.css
@ -16,26 +14,28 @@ mix
.less('resources/assets/less/AdminLTE.less', 'css') .less('resources/assets/less/AdminLTE.less', 'css')
.less('resources/assets/less/app.less', 'css') .less('resources/assets/less/app.less', 'css')
.less('resources/assets/less/overrides.less', 'css') .less('resources/assets/less/overrides.less', 'css')
.styles([
mix.styles([ './resources/assets/css/app.css',
'build/css/app.css',
'public/css/AdminLTE.css', 'public/css/AdminLTE.css',
'resources/assets/css/font-awesome/font-awesome.min.css', 'resources/assets/css/font-awesome/font-awesome.min.css',
'./bower_components/iCheck/skins/minimal/minimal.css', './bower_components/iCheck/skins/minimal/minimal.css',
'./node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.standalone.css', './node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.standalone.css',
'public/css/bootstrap-tables-sticky-header.css', 'public/css/bootstrap-tables-sticky-header.css',
'public/css/overrides.css' 'public/css/overrides.css'
], 'public/css/dist/all.css'); ],
'./public/css/dist/all.css')
mix.js( // jQuery is loaded from vue.js webpack process
// jQuery is loaded from vue.js webpack process // This compiles the vue.js file in the build directory
'./resources/assets/js/vue.js', //this is Snipe-IT's initializer for Vue.js // for later concatenation in the scripts() section below.
'build' .js(
).sourceMaps();
mix.scripts([ 'resources/assets/js/vue.js', // Snipe-IT's initializer for Vue.js
'./public/js/build'
).sourceMaps()
.scripts([
'./node_modules/jquery-ui/jquery-ui.js', './node_modules/jquery-ui/jquery-ui.js',
'build/vue.js', //this is the modularized nifty Vue.js thing we just built, above! './public/build/vue.js', //this is the modularized nifty Vue.js thing we just built, above!
'./node_modules/tether/dist/js/tether.min.js', './node_modules/tether/dist/js/tether.min.js',
'./node_modules/jquery-slimscroll/jquery.slimscroll.js', './node_modules/jquery-slimscroll/jquery.slimscroll.js',
'./node_modules/jquery.iframe-transport/jquery.iframe-transport.js', './node_modules/jquery.iframe-transport/jquery.iframe-transport.js',
@ -48,10 +48,9 @@ mix.scripts([
'./resources/assets/js/app.js', //this is part of AdminLTE './resources/assets/js/app.js', //this is part of AdminLTE
'./resources/assets/js/snipeit.js', //this is the actual Snipe-IT JS './resources/assets/js/snipeit.js', //this is the actual Snipe-IT JS
'./resources/assets/js/snipeit_modals.js' './resources/assets/js/snipeit_modals.js'
],'public/js/dist/all.js'); ],
'./public/js/dist/all.js');
//if (mix.config.inProduction) {
//mix.version();
//}
mix.copy('./public/css/dist/all.css', './public/css/build/all.css').copy('./public/js/dist/all.js', './public/js/build/all.js');
mix.version();