diff --git a/.all-contributorsrc b/.all-contributorsrc
index 1d177a4709..df3b433a92 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -893,6 +893,24 @@
"bug",
"doc"
]
+ },
+ {
+ "login": "lea-mink",
+ "name": "lea-mink",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/37537300?v=4",
+ "profile": "https://github.com/lea-mink",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "hannahtinkler",
+ "name": "Hannah Tinkler",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/7140719?v=4",
+ "profile": "https://github.com/hannahtinkler",
+ "contributions": [
+ "code"
+ ]
}
]
}
diff --git a/Dockerfile b/Dockerfile
index 62a1e4fcbb..d7752ff100 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -64,7 +64,10 @@ RUN chown -R docker /var/www/html
RUN \
rm -r "/var/www/html/storage/private_uploads" && ln -fs "/var/lib/snipeit/data/private_uploads" "/var/www/html/storage/private_uploads" \
&& rm -rf "/var/www/html/public/uploads" && ln -fs "/var/lib/snipeit/data/uploads" "/var/www/html/public/uploads" \
- && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups"
+ && rm -r "/var/www/html/storage/app/backups" && ln -fs "/var/lib/snipeit/dumps" "/var/www/html/storage/app/backups" \
+ && mkdir "/var/lib/snipeit/keys" && ln -fs "/var/lib/snipeit/keys/oauth-private.key" "/var/www/html/storage/oauth-private.key" \
+ && ln -fs "/var/lib/snipeit/keys/oauth-public.key" "/var/www/html/storage/oauth-public.key" \
+ && chown docker "/var/lib/snipeit/keys/"
############## DEPENDENCIES via COMPOSER ###################
diff --git a/README.md b/README.md
index 00faead23f..83dfc0175e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[](https://travis-ci.org/snipe/snipe-it) [](https://crowdin.com/project/snipe-it) [](https://gitter.im/snipe/snipe-it?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://hub.docker.com/r/snipe/snipe-it/) [](https://twitter.com/snipeyhead) [](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&utm_medium=referral&utm_content=snipe/snipe-it&utm_campaign=Badge_Grade)
-[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
+[](#contributors) [](https://www.codetriage.com/snipe/snipe-it)
## Snipe-IT - Open Source Asset Management System
@@ -81,7 +81,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [
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") | [
Ryan](https://github.com/Gelob)
[📖](https://github.com/snipe/snipe-it/commits?author=Gelob "Documentation") | [
vcordes79](https://github.com/vcordes79)
[💻](https://github.com/snipe/snipe-it/commits?author=vcordes79 "Code") |
-| [
fordster78](https://github.com/fordster78)
[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [
CronKz](https://github.com/CronKz)
[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [
Tim Bishop](https://github.com/tdb)
[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [
Sean McIlvenna](https://www.seanmcilvenna.com)
[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [
cepacs](https://github.com/cepacs)
[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") |
+| [
fordster78](https://github.com/fordster78)
[💻](https://github.com/snipe/snipe-it/commits?author=fordster78 "Code") | [
CronKz](https://github.com/CronKz)
[💻](https://github.com/snipe/snipe-it/commits?author=CronKz "Code") | [
Tim Bishop](https://github.com/tdb)
[💻](https://github.com/snipe/snipe-it/commits?author=tdb "Code") | [
Sean McIlvenna](https://www.seanmcilvenna.com)
[💻](https://github.com/snipe/snipe-it/commits?author=seanmcilvenna "Code") | [
cepacs](https://github.com/cepacs)
[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Acepacs "Bug reports") [📖](https://github.com/snipe/snipe-it/commits?author=cepacs "Documentation") | [
lea-mink](https://github.com/lea-mink)
[💻](https://github.com/snipe/snipe-it/commits?author=lea-mink "Code") | [
Hannah Tinkler](https://github.com/hannahtinkler)
[💻](https://github.com/snipe/snipe-it/commits?author=hannahtinkler "Code") |
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 a46e841302..8cedb2c67e 100644
--- a/app/Http/Controllers/Api/AssetsController.php
+++ b/app/Http/Controllers/Api/AssetsController.php
@@ -77,6 +77,9 @@ class AssetsController extends Controller
'last_audit_date',
'next_audit_date',
'warranty_months',
+ 'checkouts_count',
+ 'checkins_count',
+ 'user_requests_count',
];
$filter = array();
@@ -92,11 +95,7 @@ class AssetsController extends Controller
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
- 'model.category', 'model.manufacturer', 'model.fieldset','supplier');
-
-
-
-
+ 'model.category', 'model.manufacturer', 'model.fieldset','supplier')->withCount('checkins', 'checkouts', 'userRequests');
// These are used by the API to query against specific ID numbers.
@@ -106,6 +105,10 @@ class AssetsController extends Controller
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
+ if ($request->input('requestable')=='true') {
+ $assets->where('assets.requestable', '=', '1');
+ }
+
if ($request->has('model_id')) {
$assets->InModelList([$request->input('model_id')]);
}
@@ -116,7 +119,6 @@ class AssetsController extends Controller
if ($request->has('location_id')) {
$assets->where('assets.location_id', '=', $request->input('location_id'));
- // dd($assets->toSql());
}
if ($request->has('supplier_id')) {
@@ -322,7 +324,7 @@ class AssetsController extends Controller
*/
public function show($id)
{
- if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->findOrFail($id)) {
+ if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()->withCount('checkins', 'checkouts', 'userRequests')->findOrFail($id)) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
@@ -736,5 +738,50 @@ class AssetsController extends Controller
+ }
+
+
+
+ /**
+ * Returns JSON listing of all requestable assets
+ *
+ * @author [A. Gianotto] []
+ * @since [v4.0]
+ * @return JsonResponse
+ */
+ public function requestable(Request $request)
+ {
+
+ $assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
+ ->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
+ 'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
+
+ $offset = request('offset', 0);
+ $limit = $request->input('limit', 50);
+ $order = $request->input('order') === 'asc' ? 'asc' : 'desc';
+ $assets->TextSearch($request->input('search'));
+
+ switch ($request->input('sort')) {
+ case 'model':
+ $assets->OrderModels($order);
+ break;
+ case 'model_number':
+ $assets->OrderModelNumber($order);
+ break;
+ case 'category':
+ $assets->OrderCategory($order);
+ break;
+ case 'manufacturer':
+ $assets->OrderManufacturer($order);
+ break;
+ default:
+ $assets->orderBy('assets.created_at', $order);
+ break;
+ }
+
+
+ $total = $assets->count();
+ $assets = $assets->skip($offset)->take($limit)->get();
+ return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
}
diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php
index 4b18a259e2..7daea084e2 100644
--- a/app/Http/Controllers/Api/ComponentsController.php
+++ b/app/Http/Controllers/Api/ComponentsController.php
@@ -35,6 +35,10 @@ class ComponentsController extends Controller
$components->where('company_id','=',$request->input('company_id'));
}
+ if ($request->has('category_id')) {
+ $components->where('category_id','=',$request->input('category_id'));
+ }
+
$offset = request('offset', 0);
$limit = request('limit', 50);
diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php
new file mode 100644
index 0000000000..378945f855
--- /dev/null
+++ b/app/Http/Controllers/Api/ProfileController.php
@@ -0,0 +1,44 @@
+]
+ * @since [v4.3.0]
+ *
+ * @return Array
+ */
+ public function requestedAssets()
+ {
+ $checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
+
+ $results = [];
+ $results['total'] = $checkoutRequests->count();
+
+
+ foreach ($checkoutRequests as $checkoutRequest) {
+ $results['rows'][] = [
+ 'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
+ 'name' => $checkoutRequest->itemRequested()->present()->name(),
+ 'type' => $checkoutRequest->itemType(),
+ 'qty' => $checkoutRequest->quantity,
+ 'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
+ 'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
+ 'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
+ ];
+ }
+ return $results;
+ }
+
+
+}
diff --git a/app/Http/Controllers/Api/SettingsController.php b/app/Http/Controllers/Api/SettingsController.php
index 0d44337e5d..2a4b757674 100644
--- a/app/Http/Controllers/Api/SettingsController.php
+++ b/app/Http/Controllers/Api/SettingsController.php
@@ -134,12 +134,6 @@ class SettingsController extends Controller
if (!config('app.lock_passwords')) {
try {
Notification::send(Setting::first(), new MailTest());
-
- /*Mail::send('emails.test', [], function ($m) {
- $m->to(config('mail.reply_to.address'), config('mail.reply_to.name'));
- $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
- $m->subject(trans('mail.test_email'));
- });*/
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
} catch (Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
diff --git a/app/Http/Controllers/Api/SuppliersController.php b/app/Http/Controllers/Api/SuppliersController.php
index 96a1efe224..d7d54dd6fc 100644
--- a/app/Http/Controllers/Api/SuppliersController.php
+++ b/app/Http/Controllers/Api/SuppliersController.php
@@ -25,7 +25,7 @@ class SuppliersController extends Controller
$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','image')
+ array('id','name','address','address2','city','state','country','fax', 'phone','email','contact','created_at','updated_at','deleted_at','image','notes')
)->withCount('assets')->withCount('licenses')->withCount('accessories')->whereNull('deleted_at');
diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php
index 5d075b32d2..73aeb2a993 100644
--- a/app/Http/Controllers/Api/UsersController.php
+++ b/app/Http/Controllers/Api/UsersController.php
@@ -194,7 +194,9 @@ class UsersController extends Controller
$this->authorize('view', User::class);
$user = new User;
$user->fill($request->all());
- $user->password = bcrypt($request->input('password'));
+
+ $tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
+ $user->password = bcrypt($request->get('password', $tmp_pass));
if ($user->save()) {
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php
index 7a5dcf1a4d..280a841e96 100755
--- a/app/Http/Controllers/AssetsController.php
+++ b/app/Http/Controllers/AssetsController.php
@@ -1305,12 +1305,16 @@ class AssetsController extends Controller
}
}
- public function getRequestedIndex($id = null)
+ public function getRequestedIndex($user_id = null)
{
- if ($id) {
- $requestedItems = CheckoutRequest::where('user_id', $id)->with('user', 'requestedItem')->get();
+ $requestedItems = CheckoutRequest::with('user', 'requestedItem')->whereNull('canceled_at')->with('user', 'requestedItem');
+
+ if ($user_id) {
+ $requestedItems->where('user_id', $user_id)->get();
}
- $requestedItems = CheckoutRequest::with('user', 'requestedItem')->get();
+
+ $requestedItems = $requestedItems->orderBy('created_at', 'desc')->get();
+
return view('hardware/requested', compact('requestedItems'));
}
diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php
index e0a1f5aa58..507498afc4 100644
--- a/app/Http/Controllers/ReportsController.php
+++ b/app/Http/Controllers/ReportsController.php
@@ -503,6 +503,19 @@ class ReportsController extends Controller
$header[] = trans('general.zip');
}
+ if ($request->has('rtd_location')) {
+ $header[] = trans('admin/hardware/form.default_location');
+ }
+ if ($request->has('rtd_location_address')) {
+ $header[] = trans('general.address');
+ $header[] = trans('general.address');
+ $header[] = trans('general.city');
+ $header[] = trans('general.state');
+ $header[] = trans('general.country');
+ $header[] = trans('general.zip');
+ }
+
+
if ($request->has('assigned_to')) {
$header[] = trans('admin/hardware/table.checkoutto');
$header[] = trans('general.type');
@@ -516,6 +529,10 @@ class ReportsController extends Controller
$header[] = 'Employee No.';
}
+ if ($request->has('department')) {
+ $header[] = trans('general.department');
+ }
+
if ($request->has('status')) {
$header[] = trans('general.status');
}
@@ -671,12 +688,25 @@ class ReportsController extends Controller
}
if ($request->has('location_address')) {
- $row[] = ($asset->location) ? $asset->location->address : '';
- $row[] = ($asset->location) ? $asset->location->address2 : '';
- $row[] = ($asset->location) ? $asset->location->city : '';
- $row[] = ($asset->location) ? $asset->location->state : '';
- $row[] = ($asset->location) ? $asset->location->country : '';
- $row[] = ($asset->location) ? $asset->location->zip : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address2 : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->city : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->state : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->country : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->zip : '';
+ }
+
+ if ($request->has('rtd_location')) {
+ $row[] = ($asset->location) ? $asset->defaultLoc->present()->name() : '';
+ }
+
+ if ($request->has('rtd_location_address')) {
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->address2 : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->city : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->state : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->country : '';
+ $row[] = ($asset->defaultLoc) ? $asset->defaultLoc->zip : '';
}
@@ -703,6 +733,15 @@ class ReportsController extends Controller
}
}
+
+ if ($request->has('department')) {
+ if ($asset->checkedOutToUser()) {
+ $row[] = (($asset->assignedto) && ($asset->assignedto->department)) ? $asset->assignedto->department->name : '';
+ } else {
+ $row[] = ''; // Empty string if unassigned
+ }
+ }
+
if ($request->has('status')) {
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
}
diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php
index 96f893042e..b0f0dae15f 100755
--- a/app/Http/Controllers/ViewAssetsController.php
+++ b/app/Http/Controllers/ViewAssetsController.php
@@ -12,6 +12,8 @@ use App\Models\Consumable;
use App\Models\License;
use App\Models\Setting;
use App\Models\User;
+use App\Notifications\RequestAssetNotification;
+use App\Notifications\RequestAssetCancelationNotification;
use Auth;
use Config;
use DB;
@@ -80,116 +82,71 @@ class ViewAssetsController extends Controller
{
$item = null;
$fullItemType = 'App\\Models\\' . studly_case($itemType);
+
if ($itemType == "asset_model") {
$itemType = "model";
}
$item = call_user_func(array($fullItemType, 'find'), $itemId);
+
$user = Auth::user();
- $quantity = $data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
+
$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $item->id;
$logaction->item_type = $fullItemType;
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
+
if ($user->location_id) {
$logaction->location_id = $user->location_id;
}
$logaction->target_id = $data['user_id'] = Auth::user()->id;
$logaction->target_type = User::class;
+ $data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
$data['requested_by'] = $user->present()->fullName();
- $data['item_name'] = $item->name;
+ $data['item'] = $item;
$data['item_type'] = $itemType;
+ $data['target'] = Auth::user();
+
if ($fullItemType == Asset::class) {
$data['item_url'] = route('hardware.show', $item->id);
- $slackMessage = ' Asset <'.url('/').'/hardware/'.$item->id.'/view'.'|'.$item->present()->name().'> requested by <'.url('/').'/users/'.$item->user_id.'/view'.'|'.$user->present()->fullName().'>.';
} else {
$data['item_url'] = route("view/${itemType}", $item->id);
- $slackMessage = $quantity. ' ' . class_basename(strtoupper($logaction->item_type)).' <'.$data['item_url'].'|'.$item->name.'> requested by <'.url('/').'/user/'.$item->id.'/view'.'|'.$user->present()->fullName().'>.';
+
}
$settings = Setting::getSettings();
- if ($settings->slack_endpoint) {
-
- $slack_settings = [
- 'username' => $settings->botname,
- 'channel' => $settings->slack_channel,
- 'link_names' => true
- ];
-
- $slackClient = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
- }
-
- if ($item->isRequestedBy($user)) {
-
- $item->cancelRequest();
- $log = $logaction->logaction('request_canceled');
+ if ($item_request = $item->isRequestedBy($user)) {
+ $item->cancelRequest();
+ $data['item_quantity'] = $item_request->qty;
+ $logaction->logaction('request_canceled');
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
- Mail::send('emails.asset-canceled', $data, function ($m) use ($user, $settings) {
- $m->to(explode(',', $settings->alert_email), $settings->site_name);
- $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
- $m->subject(trans('mail.Item_Request_Canceled'));
- });
- }
-
- if ($settings->slack_endpoint) {
- try {
- $slackClient->attach([
- 'color' => 'good',
- 'fields' => [
- [
- 'title' => 'CANCELED:',
- 'value' => $slackMessage
- ]
-
- ]
- ])->send('Item Request Canceled');
-
- } catch (Exception $e) {
-
- }
+ $settings->notify(new RequestAssetCancelationNotification($data));
}
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
} else {
$item->request();
-
- $log = $logaction->logaction('requested');
-
-
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
- Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
- $m->to(explode(',', $settings->alert_email), $settings->site_name);
- $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
- $m->subject(trans('mail.Item_Requested'));
- });
+ $logaction->logaction('requested');
+ $settings->notify(new RequestAssetNotification($data));
}
- if ($settings->slack_endpoint) {
- try {
- $slackClient->attach([
- 'color' => 'good',
- 'fields' => [
- [
- 'title' => 'REQUESTED:',
- 'value' => $slackMessage
- ]
- ]
- ])->send('Item Requested');
-
- } catch (Exception $e) {
-
- }
- }
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
}
}
+
+
+
+
+
+
public function getRequestAsset($assetId = null)
{
@@ -197,74 +154,45 @@ class ViewAssetsController extends Controller
// Check if the asset exists and is requestable
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
- // Redirect to the asset management page
- return redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
+ return redirect()->route('requestable-assets')
+ ->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
} elseif (!Company::isCurrentUserHasAccess($asset)) {
- return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions'));
+ return redirect()->route('requestable-assets')
+ ->with('error', trans('general.insufficient_permissions'));
}
- // If it's requested, cancel the request.
+
+ $data['item'] = $asset;
+ $data['target'] = Auth::user();
+ $data['item_quantity'] = 1;
+ $settings = Setting::getSettings();
+
+ $logaction = new Actionlog();
+ $logaction->item_id = $data['asset_id'] = $asset->id;
+ $logaction->item_type = $data['item_type'] = Asset::class;
+ $logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
+
+ if ($user->location_id) {
+ $logaction->location_id = $user->location_id;
+ }
+ $logaction->target_id = $data['user_id'] = Auth::user()->id;
+ $logaction->target_type = User::class;
+
+
+ // If it's already requested, cancel the request.
if ($asset->isRequestedBy(Auth::user())) {
$asset->cancelRequest();
- return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
+ $logaction->logaction('request canceled');
+ $settings->notify(new RequestAssetCancelationNotification($data));
+ return redirect()->route('requestable-assets')
+ ->with('success')->with('success', trans('admin/hardware/message.requests.cancel-success'));
} else {
- $logaction = new Actionlog();
- $logaction->item_id = $data['asset_id'] = $asset->id;
- $logaction->item_type = Asset::class;
- $logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
- $data['asset_type'] = 'hardware';
- if ($user->location_id) {
- $logaction->location_id = $user->location_id;
- }
- $logaction->target_id = $data['user_id'] = Auth::user()->id;
- $logaction->target_type = User::class;
- $log = $logaction->logaction('requested');
-
- $data['requested_by'] = $user->present()->fullName();
- $data['asset_name'] = $asset->present()->name();
-
- $settings = Setting::getSettings();
-
- if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
- Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
- $m->to(explode(',', $settings->alert_email), $settings->site_name);
- $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
- $m->subject(trans('mail.asset_requested'));
- });
- }
+ $logaction->logaction('requested');
$asset->request();
+ $settings->notify(new RequestAssetNotification($data));
- if ($settings->slack_endpoint) {
-
-
- $slack_settings = [
- 'username' => $settings->botname,
- 'channel' => $settings->slack_channel,
- 'link_names' => true
- ];
-
- $client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
-
- try {
- $client->attach([
- 'color' => 'good',
- 'fields' => [
- [
- 'title' => 'REQUESTED:',
- 'value' => class_basename(strtoupper($logaction->item_type)).' asset <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.$asset->present()->name().'> requested by <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.Auth::user()->present()->fullName().'>.'
- ]
-
- ]
- ])->send('Asset Requested');
-
- } catch (Exception $e) {
-
- }
-
- }
-
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
}
@@ -273,13 +201,10 @@ class ViewAssetsController extends Controller
public function getRequestedAssets()
{
- $checkoutrequests = CheckoutRequest::all();
-
- return view('account/requested-items', compact($checkoutrequests));
+ return view('account/requested');
}
-
// Get the acceptance screen
public function getAcceptAsset($logID = null)
{
diff --git a/app/Http/Requests/SaveUserRequest.php b/app/Http/Requests/SaveUserRequest.php
index d498f592c8..b721ed0e9e 100644
--- a/app/Http/Requests/SaveUserRequest.php
+++ b/app/Http/Requests/SaveUserRequest.php
@@ -35,7 +35,10 @@ class SaveUserRequest extends Request
{
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
- $rules['password'] = Setting::passwordComplexityRulesSaving('store');
+ if ($this->request->get('ldap_import') == false)
+ {
+ $rules['password'] = Setting::passwordComplexityRulesSaving('store');
+ }
break;
}
diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php
index 3877f7bc2e..83448e4836 100644
--- a/app/Http/Transformers/AssetsTransformer.php
+++ b/app/Http/Transformers/AssetsTransformer.php
@@ -77,6 +77,9 @@ class AssetsTransformer
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost),
+ 'checkins_count' => (int) $asset->checkins_count,
+ 'checkouts_count' => (int) $asset->checkouts_count,
+ 'user_requests_count' => (int) $asset->user_requests_count,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
];
@@ -160,4 +163,39 @@ class AssetsTransformer
'type' => $asset->assignedType()
] : null;
}
+
+
+ public function transformRequestedAssets(Collection $assets, $total)
+ {
+ $array = array();
+ foreach ($assets as $asset) {
+ $array[] = self::transformRequestedAsset($asset);
+ }
+ return (new DatatablesTransformer)->transformDatatables($array, $total);
+ }
+
+ public function transformRequestedAsset(Asset $asset) {
+ $array = [
+ 'id' => (int) $asset->id,
+ 'name' => e($asset->name),
+ 'asset_tag' => e($asset->asset_tag),
+ 'serial' => e($asset->serial),
+ 'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
+ 'model' => ($asset->model) ? e($asset->model->name) : null,
+ 'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
+ 'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
+ 'location' => ($asset->location) ? e($asset->location->name) : null,
+ 'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
+ ];
+
+ $permissions_array['available_actions'] = [
+ 'cancel' => ($asset->isRequestedBy(\Auth::user())) ? true : false,
+ 'request' => ($asset->isRequestedBy(\Auth::user())) ? false : true,
+
+ ];
+
+ $array += $permissions_array;
+ return $array;
+
+ }
}
diff --git a/app/Importer/Importer.php b/app/Importer/Importer.php
index 45f7d1d170..db7dc938f0 100644
--- a/app/Importer/Importer.php
+++ b/app/Importer/Importer.php
@@ -119,10 +119,13 @@ abstract class Importer
// Remove any custom fields that do not exist in the header row. This prevents nulling out values that shouldn't exist.
// In detail, we compare the lower case name of custom fields (indexed by name) to the keys in the header row. This
// results in an array with only custom fields that are in the file.
- $this->customFields = array_intersect_key(
- array_change_key_case($this->customFields),
- array_change_key_case(array_flip($headerRow))
- );
+ if ($this->customFields) {
+ $this->customFields = array_intersect_key(
+ array_change_key_case($this->customFields),
+ array_change_key_case(array_flip($headerRow))
+ );
+ }
+
DB::transaction(function () use (&$results) {
@@ -244,6 +247,18 @@ abstract class Importer
$user_username = $this->findCsvMatch($row, "username");
$first_name = '';
$last_name = '';
+ if(empty($user_name) && empty($user_email) && empty($user_username)) {
+ $this->log('No user data provided - skipping user creation, just adding asset');
+ //$user_username = '';
+ return false;
+ }
+ // A username was given.
+ if( !empty($user_username)) {
+ $user = User::where('username', $user_username)->first();
+ if($user) {
+ return $user;
+ }
+ }
// A number was given instead of a name
if (is_numeric($user_name)) {
$this->log('User '.$user_name.' is not a name - assume this user already exists');
@@ -252,28 +267,24 @@ abstract class Importer
return $user;
}
$this->log('User with id'.$user_name.' does not exist. Continuing through our processes');
- } elseif (empty($user_name)) {
- $this->log('No user data provided - skipping user creation, just adding asset');
- //$user_username = '';
- return false;
- } else {
- $user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
- $first_name = $user_email_array['first_name'];
- $last_name = $user_email_array['last_name'];
+ }
+ // Generate data based on user name.
+ $user_email_array = User::generateFormattedNameFromFullName(Setting::getSettings()->email_format, $user_name);
+ $first_name = $user_email_array['first_name'];
+ $last_name = $user_email_array['last_name'];
- if ($user_email=='') {
- if (Setting::getSettings()->email_domain) {
- $user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
- }
+ if (empty($user_email)) {
+ if (Setting::getSettings()->email_domain) {
+ $user_email = str_slug($user_email_array['username']).'@'.Setting::getSettings()->email_domain;
}
+ }
- if ($user_username=='') {
- if ($this->usernameFormat =='email') {
- $user_username = $user_email;
- } else {
- $user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
- $user_username = $user_name_array['username'];
- }
+ if (empty($user_username)) {
+ if ($this->usernameFormat =='email') {
+ $user_username = $user_email;
+ } else {
+ $user_name_array = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $user_name);
+ $user_username = $user_name_array['username'];
}
}
$user = new User;
diff --git a/app/Models/Asset.php b/app/Models/Asset.php
index e7279ebd61..d50024448d 100644
--- a/app/Models/Asset.php
+++ b/app/Models/Asset.php
@@ -11,7 +11,6 @@ use Config;
use Illuminate\Database\Eloquent\SoftDeletes;
use Log;
use Watson\Validating\ValidatingTrait;
-use Illuminate\Notifications\Notifiable;
use DB;
use App\Notifications\CheckinAssetNotification;
use App\Notifications\CheckoutAssetNotification;
@@ -95,6 +94,7 @@ class Asset extends Depreciable
'assigned_type',
'company_id',
'image',
+ 'location_id',
'model_id',
'name',
'notes',
@@ -350,6 +350,38 @@ class Asset extends Depreciable
->withTrashed();
}
+ /**
+ * Get checkouts
+ */
+ public function checkouts()
+ {
+ return $this->assetlog()->where('action_type', '=', 'checkout')
+ ->orderBy('created_at', 'desc')
+ ->withTrashed();
+ }
+
+ /**
+ * Get checkins
+ */
+ public function checkins()
+ {
+ return $this->assetlog()
+ ->where('action_type', '=', 'checkin from')
+ ->orderBy('created_at', 'desc')
+ ->withTrashed();
+ }
+
+ /**
+ * Get user requests
+ */
+ public function userRequests()
+ {
+ return $this->assetlog()
+ ->where('action_type', '=', 'requested')
+ ->orderBy('created_at', 'desc')
+ ->withTrashed();
+ }
+
/**
* assetmaintenances
diff --git a/app/Models/AssetMaintenance.php b/app/Models/AssetMaintenance.php
index 8a07ec0dbf..a99fa4ebd6 100644
--- a/app/Models/AssetMaintenance.php
+++ b/app/Models/AssetMaintenance.php
@@ -52,7 +52,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
return [
trans('admin/asset_maintenances/general.maintenance') => trans('admin/asset_maintenances/general.maintenance'),
trans('admin/asset_maintenances/general.repair') => trans('admin/asset_maintenances/general.repair'),
- trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade')
+ trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade'),
+ 'PAT test' => 'PAT test',
];
}
diff --git a/app/Models/AssetModel.php b/app/Models/AssetModel.php
index 1dec88a5b0..48c86686c9 100755
--- a/app/Models/AssetModel.php
+++ b/app/Models/AssetModel.php
@@ -98,6 +98,14 @@ class AssetModel extends SnipeModel
return $this->belongsTo('\App\Models\CustomFieldset', 'fieldset_id');
}
+
+ public function getImageUrl() {
+ if ($this->image) {
+ return url('/').'/uploads/models/'.$this->image;
+ }
+ return false;
+ }
+
/**
* -----------------------------------------------
* BEGIN QUERY SCOPES
diff --git a/app/Models/CheckoutRequest.php b/app/Models/CheckoutRequest.php
index 668f326402..df40618317 100644
--- a/app/Models/CheckoutRequest.php
+++ b/app/Models/CheckoutRequest.php
@@ -1,12 +1,13 @@
'required|min:2|max:255|unique_undeleted',
- 'city' => 'min:3|max:255|nullable',
+ 'city' => 'min:2|max:255|nullable',
'country' => 'min:2|max:2|nullable',
'address' => 'max:80|nullable',
'address2' => 'max:80|nullable',
diff --git a/app/Models/Requestable.php b/app/Models/Requestable.php
index 878c3b69a8..96a45c42f6 100644
--- a/app/Models/Requestable.php
+++ b/app/Models/Requestable.php
@@ -19,9 +19,7 @@ trait Requestable
public function isRequestedBy(User $user)
{
- $requests = $this->requests->where('user_id', $user->id);
-
- return $requests->count() > 0;
+ return $this->requests->where('canceled_at', NULL)->where('user_id', $user->id)->first();
}
public function scopeRequestedBy($query, User $user)
@@ -31,15 +29,20 @@ trait Requestable
});
}
- public function request()
+ public function request($qty = 1)
{
$this->requests()->save(
- new CheckoutRequest(['user_id' => Auth::id()])
+ new CheckoutRequest(['user_id' => Auth::id(), 'qty' => $qty])
);
}
+ public function deleteRequest()
+ {
+ $this->requests()->where('user_id', Auth::id())->delete();
+ }
+
public function cancelRequest()
{
- $this->requests()->where('user_id', Auth::id())->delete();
+ $this->requests()->where('user_id', Auth::id())->update(['canceled_at' => \Carbon\Carbon::now()]);
}
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 4c014f9cea..c2ccd35be6 100755
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -39,6 +39,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
'first_name',
'jobtitle',
'last_name',
+ 'ldap_import',
'locale',
'location_id',
'manager_id',
@@ -292,7 +293,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
*/
public function checkoutRequests()
{
- return $this->belongsToMany(Asset::class, 'checkout_requests');
+ return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at');
}
public function throttle()
diff --git a/app/Notifications/RequestAssetCancelation.php b/app/Notifications/RequestAssetCancelation.php
new file mode 100644
index 0000000000..3902f9af3b
--- /dev/null
+++ b/app/Notifications/RequestAssetCancelation.php
@@ -0,0 +1,137 @@
+target = $params['target'];
+ $this->item = $params['item'];
+ $this->note = '';
+ $this->last_checkout = '';
+ $this->item_quantity = $params['item_quantity'];
+ $this->expected_checkin = '';
+ $this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
+ false);
+ $this->settings = Setting::getSettings();
+
+ if (array_key_exists('note', $params)) {
+ $this->note = $params['note'];
+ }
+
+ if ($this->item->last_checkout) {
+ $this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
+ false);
+ }
+
+ if ($this->item->expected_checkin) {
+ $this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
+ false);
+ }
+
+
+ }
+
+ /**
+ * Get the notification's delivery channels.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function via()
+ {
+
+ $notifyBy = [];
+
+ if (Setting::getSettings()->slack_endpoint!='') {
+ \Log::debug('use slack');
+ $notifyBy[] = 'slack';
+ }
+
+
+ $notifyBy[] = 'mail';
+
+ return $notifyBy;
+ }
+
+ public function toSlack()
+ {
+
+
+ $target = $this->target;
+ $item = $this->item;
+ $note = $this->note;
+ $qty = $this->item_quantity;
+ $botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
+
+ $fields = [
+ 'QTY' => $qty,
+ 'Canceled By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
+ ];
+
+ if (($this->expected_checkin) && ($this->expected_checkin!='')) {
+ $fields['Expected Checkin'] = $this->expected_checkin;
+ }
+
+ return (new SlackMessage)
+ ->content( trans('mail.a_user_canceled'))
+ ->from($botname)
+ ->attachment(function ($attachment) use ($item, $note, $fields) {
+ $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
+ ->fields($fields)
+ ->content($note);
+ });
+ }
+ /**
+ * Get the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail()
+ {
+
+ $fields = [];
+
+ // Check if the item has custom fields associated with it
+ if (($this->item->model) && ($this->item->model->fieldset)) {
+ $fields = $this->item->model->fieldset->fields;
+ }
+
+ $message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
+ [
+ 'item' => $this->item,
+ 'note' => $this->note,
+ 'requested_by' => $this->target,
+ 'requested_date' => $this->requested_date,
+ 'fields' => $fields,
+ 'qty' => $this->item_quantity,
+ 'last_checkout' => $this->last_checkout,
+ 'expected_checkin' => $this->expected_checkin,
+ 'intro_text' => trans('mail.a_user_canceled'),
+ ])
+ ->subject(trans('Item Request Canceled'));
+
+
+ return $message;
+
+
+ }
+
+}
diff --git a/app/Notifications/RequestAssetNotification.php b/app/Notifications/RequestAssetNotification.php
new file mode 100644
index 0000000000..eac2cef22f
--- /dev/null
+++ b/app/Notifications/RequestAssetNotification.php
@@ -0,0 +1,134 @@
+target = $params['target'];
+ $this->item = $params['item'];
+ $this->item_type = $params['item_type'];
+ $this->item_quantity = $params['item_quantity'];
+ $this->note = '';
+ $this->last_checkout = '';
+ $this->expected_checkin = '';
+ $this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
+ false);
+ $this->settings = Setting::getSettings();
+
+ if (array_key_exists('note', $params)) {
+ $this->note = $params['note'];
+ }
+
+ if ($this->item->last_checkout) {
+ $this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
+ false);
+ }
+
+ if ($this->item->expected_checkin) {
+ $this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
+ false);
+ }
+
+
+ }
+
+ /**
+ * Get the notification's delivery channels.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function via()
+ {
+
+ $notifyBy = [];
+
+ if (Setting::getSettings()->slack_endpoint!='') {
+ \Log::debug('use slack');
+ $notifyBy[] = 'slack';
+ }
+
+
+ $notifyBy[] = 'mail';
+
+ return $notifyBy;
+ }
+
+ public function toSlack()
+ {
+
+
+ $target = $this->target;
+ $qty = $this->item_quantity;
+ $item = $this->item;
+ $note = $this->note;
+ $botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
+
+ $fields = [
+ 'QTY' => $qty,
+ 'Requested By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
+ ];
+
+ return (new SlackMessage)
+ ->content(trans('mail.Item_Requested'))
+ ->from($botname)
+ ->attachment(function ($attachment) use ($item, $note, $fields) {
+ $attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
+ ->fields($fields)
+ ->content($note);
+ });
+ }
+ /**
+ * Get the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail()
+ {
+
+ $fields = [];
+
+ // Check if the item has custom fields associated with it
+ if (($this->item->model) && ($this->item->model->fieldset)) {
+ $fields = $this->item->model->fieldset->fields;
+ }
+
+ $message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
+ [
+ 'item' => $this->item,
+ 'note' => $this->note,
+ 'requested_by' => $this->target,
+ 'requested_date' => $this->requested_date,
+ 'fields' => $fields,
+ 'last_checkout' => $this->last_checkout,
+ 'expected_checkin' => $this->expected_checkin,
+ 'intro_text' => trans('mail.a_user_requested'),
+ 'qty' => $this->item_quantity,
+ ])
+ ->subject(trans('mail.Item_Requested'));
+
+
+ return $message;
+
+
+ }
+
+}
diff --git a/app/Presenters/AssetMaintenancesPresenter.php b/app/Presenters/AssetMaintenancesPresenter.php
index b3156fb57a..2c8917423d 100644
--- a/app/Presenters/AssetMaintenancesPresenter.php
+++ b/app/Presenters/AssetMaintenancesPresenter.php
@@ -70,6 +70,11 @@ class AssetMaintenancesPresenter extends Presenter
"searchable" => true,
"sortable" => true,
"title" => trans('admin/asset_maintenances/form.asset_maintenance_type'),
+ ], [
+ "field" => "title",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/asset_maintenances/form.title'),
], [
"field" => "created_at",
"searchable" => true,
diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php
index f87819a0cb..11f00e6904 100644
--- a/app/Presenters/AssetPresenter.php
+++ b/app/Presenters/AssetPresenter.php
@@ -181,6 +181,27 @@ class AssetPresenter extends Presenter
"visible" => false,
"title" => trans('general.notes'),
+ ], [
+ "field" => "checkouts_count",
+ "searchable" => false,
+ "sortable" => true,
+ "visible" => false,
+ "title" => trans('general.checkouts_count')
+
+ ],[
+ "field" => "checkins_count",
+ "searchable" => false,
+ "sortable" => true,
+ "visible" => false,
+ "title" => trans('general.checkins_count')
+
+ ], [
+ "field" => "user_requests_count",
+ "searchable" => false,
+ "sortable" => true,
+ "visible" => false,
+ "title" => trans('general.user_requests_count')
+
], [
"field" => "created_at",
"searchable" => false,
diff --git a/config/version.php b/config/version.php
index 76dd84177b..4964822b92 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,8 +1,8 @@
'v4.2.0',
- 'full_app_version' => 'v4.2.0 - build 3497-g590938f',
- 'build_version' => '3497',
+ 'full_app_version' => 'v4.2.0 - build 3508-g086711e46',
+ 'build_version' => '3508',
'prerelease_version' => '',
'hash_version' => 'g590938f',
'full_hash' => 'v4.2.0-4-g590938f',
diff --git a/database/migrations/2018_03_29_053618_add_canceled_at_and_fulfilled_at_in_requests.php b/database/migrations/2018_03_29_053618_add_canceled_at_and_fulfilled_at_in_requests.php
new file mode 100644
index 0000000000..dd8648c666
--- /dev/null
+++ b/database/migrations/2018_03_29_053618_add_canceled_at_and_fulfilled_at_in_requests.php
@@ -0,0 +1,36 @@
+dateTime('canceled_at')->nullable()->default(null);
+ $table->dateTime('fulfilled_at')->nullable()->default(null);
+ $table->dateTime('deleted_at')->nullable()->default(null);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('checkout_requests', function (Blueprint $table) {
+ $table->dropColumn('canceled_at');
+ $table->dropColumn('fulfilled_at');
+ $table->dropColumn('deleted_at');
+ });
+ }
+}
diff --git a/database/migrations/2018_03_29_070121_add_drop_unique_requests.php b/database/migrations/2018_03_29_070121_add_drop_unique_requests.php
new file mode 100644
index 0000000000..a6299ae7a9
--- /dev/null
+++ b/database/migrations/2018_03_29_070121_add_drop_unique_requests.php
@@ -0,0 +1,32 @@
+dropUnique('checkout_requests_user_id_requestable_id_requestable_type_unique');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('checkout_requests', function (Blueprint $table) {
+ $table->index(['user_id','requestable_id','requestable_type'], 'checkout_requests_user_id_requestable_id_requestable_type_unique')->unique();
+ });
+ }
+}
diff --git a/database/migrations/2018_03_29_070511_add_new_index_requestable.php b/database/migrations/2018_03_29_070511_add_new_index_requestable.php
new file mode 100644
index 0000000000..09c06566e8
--- /dev/null
+++ b/database/migrations/2018_03_29_070511_add_new_index_requestable.php
@@ -0,0 +1,32 @@
+index(['user_id','requestable_id','requestable_type'], 'checkout_requests_user_id_requestable_id_requestable_type');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('checkout_requests', function (Blueprint $table) {
+ $table->dropIndex('checkout_requests_user_id_requestable_id_requestable_type');
+ });
+ }
+}
diff --git a/docker.env b/docker.env
deleted file mode 100644
index 88ed191ce1..0000000000
--- a/docker.env
+++ /dev/null
@@ -1,28 +0,0 @@
-# Docker Environment File
-
-#Database Variables
-MYSQL_ROOT_PASSWORD=supersecuresecretthing
-MYSQL_DATABASE=snipeit
-MYSQL_USER=snipeit
-MYSQL_PASSWORD=tinglewingler
-
-#Email Variables
-MAIL_PORT_587_TCP_ADDR=smtp.mandrillapp.com
-MAIL_PORT_587_TCP_PORT=587
-MAIL_ENV_FROM_ADDR=you@example.com
-MAIL_ENV_FROM_NAME=Snipe-IT
-MAIL_ENV_ENCRYPTION=tcp
-MAIL_ENV_USERNAME=you@example.com
-MAIL_ENV_PASSWORD=N7AqLufTZMULWHWYwJ0ZCw
-MAIL_ENV_REPLYTO_ADDR=you@example.com
-MAIL_ENV_REPLYTO_NAME='Snipe-IT'
-
-#SNIPE-IT Variables
-APP_ENV=develop
-APP_DEBUG=true
-APP_KEY=Y5hJeC7x1i7OxhDrvrQPlB9KvCorvRdO
-APP_URL=http://127.0.0.1:32782
-APP_TIMEZONE=US/Pacific
-APP_LOCALE=en
-
-ALLOW_IFRAMING=false
diff --git a/public/css/bootstrap-table.css b/public/css/bootstrap-table.css
index dd589e5d8c..733c03a69c 100755
--- a/public/css/bootstrap-table.css
+++ b/public/css/bootstrap-table.css
@@ -50,7 +50,7 @@
.fixed-table-body {
overflow-x: auto;
overflow-y: auto;
- height: 100%;
+ height: auto;
}
.fixed-table-container table {
@@ -294,4 +294,4 @@ div.fixed-table-scroll-outer {
width: 200px;
height: 150px;
overflow: hidden;
-}
\ No newline at end of file
+}
diff --git a/public/css/signature-pad.css b/public/css/signature-pad.css
index 97e68714bc..218b9c2365 100755
--- a/public/css/signature-pad.css
+++ b/public/css/signature-pad.css
@@ -44,11 +44,10 @@
.m-signature-pad--body {
position: absolute;
- left: 20px;
- right: 20px;
top: 20px;
bottom: 60px;
border: 1px solid #f4f4f4;
+ background-color: white;
}
.m-signature-pad--body
diff --git a/public/css/skins/skin-green-dark.css b/public/css/skins/skin-green-dark.css
index 7babfe758f..0282e78e43 100644
--- a/public/css/skins/skin-green-dark.css
+++ b/public/css/skins/skin-green-dark.css
@@ -7,13 +7,6 @@
--text-sub: #BBB;
}
-.main-header .navbar, .main-header .logo {
- background-color: #18A452;
- background: -webkit-linear-gradient(top, #18A452 0%,#18A452 100%);
- background: linear-gradient(to bottom, #18A452 0%,#18A452 100%);
- border-color: #18A452;
-}
-
a, a:link, a:visited, .btn-primary.hover {
color: var(--header);
}
@@ -108,6 +101,16 @@ input[type=text], input[type=search] {
background-color: var(--back-main);
color: var(--text-main);
}
+.main-header .navbar, .main-header .logo {
+ background-color: var(--header);
+ background: -webkit-linear-gradient(top, var(--header) 0%,var(--header) 100%);
+ background: linear-gradient(to bottom, var(--header) 0%,var(--header) 100%);
+ border-color: var(--header);
+}
+.modal-content {
+ background-color: var(--back-main);
+ color: var(--text-main);
+}
.nav-tabs-custom>.nav-tabs>li>a, .nav-tabs-custom>.nav-tabs>li.active>a {
color: var(--text-main);
}
diff --git a/public/css/skins/skin-orange-dark.css b/public/css/skins/skin-orange-dark.css
index 749df63866..270ff19581 100644
--- a/public/css/skins/skin-orange-dark.css
+++ b/public/css/skins/skin-orange-dark.css
@@ -8,10 +8,14 @@
}
.main-header .navbar, .main-header .logo {
- background-color: #FF8C00;
- background: -webkit-linear-gradient(top, #FF8C00 0%,#FF8C00 100%);
- background: linear-gradient(to bottom, #FF8C00 0%,#FF8C00 100%);
- border-color: #FF8C00;
+ background-color: var(--header);
+ background: -webkit-linear-gradient(top, var(--header) 0%,var(--header) 100%);
+ background: linear-gradient(to bottom, var(--header) 0%,var(--header) 100%);
+ border-color: var(--header);
+}
+.modal-content {
+ background-color: var(--back-main);
+ color: var(--text-main);
}
a, a:link, a:visited, .btn-primary.hover {
diff --git a/public/css/skins/skin-red-dark.css b/public/css/skins/skin-red-dark.css
index 39f3490f56..bb4d67f167 100644
--- a/public/css/skins/skin-red-dark.css
+++ b/public/css/skins/skin-red-dark.css
@@ -8,10 +8,15 @@
}
.main-header .navbar, .main-header .logo {
-background-color: #ad0c0c;
-background: -webkit-linear-gradient(top, #ad0c0c 0%,#ad0c0c 100%);
-background: linear-gradient(to bottom, #ad0c0c 0%,#ad0c0c 100%);
-border-color: #ad0c0c;
+ background-color: var(--header);
+ background: -webkit-linear-gradient(top, var(--header) 0%,var(--header) 100%);
+ background: linear-gradient(to bottom, var(--header) 0%,var(--header) 100%);
+ border-color: var(--header);
+}
+
+.modal-content {
+ background-color: var(--back-main);
+ color: var(--text-main);
}
a, a:link, a:visited, .btn-primary.hover {
diff --git a/public/js/build/all.js b/public/js/build/all.js
index 0ea579b615..c1a69a16e4 100644
--- a/public/js/build/all.js
+++ b/public/js/build/all.js
@@ -1,26 +1,26 @@
-function _init(){"use strict";$.AdminLTE.layout={activate:function(){var t=this;t.fix(),t.fixSidebar(),$(window,".wrapper").resize(function(){t.fix(),t.fixSidebar()})},fix:function(){var t=$(".main-header").outerHeight()+$(".main-footer").outerHeight(),e=$(window).height(),i=$(".sidebar").height();if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",e-$(".main-footer").outerHeight());else{var n;e>=i?($(".content-wrapper, .right-side").css("min-height",e-t),n=e-t):($(".content-wrapper, .right-side").css("min-height",i),n=i);var s=$($.AdminLTE.options.controlSidebarOptions.selector);void 0!==s&&s.height()>n&&$(".content-wrapper, .right-side").css("min-height",s.height())}},fixSidebar:function(){if(!$("body").hasClass("fixed"))return void(void 0!==$.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"));void 0===$.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),$.AdminLTE.options.sidebarSlimScroll&&void 0!==$.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimscroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"}))}},$.AdminLTE.pushMenu={activate:function(t){var e=$.AdminLTE.options.screenSizes;$(t).on("click",function(t){t.preventDefault(),$(window).width()>e.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=e.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var t=this,e=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>e&&t.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>e&&t.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(t){var e=this,i=$.AdminLTE.options.animationSpeed;$(document).on("click",t+" li a",function(t){var n=$(this),s=n.next();if(s.is(".treeview-menu")&&s.is(":visible"))s.slideUp(i,function(){s.removeClass("menu-open")}),s.parent("li").removeClass("active");else if(s.is(".treeview-menu")&&!s.is(":visible")){var o=n.parents("ul").first(),r=o.find("ul:visible").slideUp(i);r.removeClass("menu-open");var a=n.parent("li");s.slideDown(i,function(){s.addClass("menu-open"),o.find("li.active").removeClass("active"),a.addClass("active"),e.layout.fix()})}s.is(".treeview-menu")&&t.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var t=this,e=$.AdminLTE.options.controlSidebarOptions,i=$(e.selector);$(e.toggleBtnSelector).on("click",function(n){n.preventDefault(),i.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?t.close(i,e.slide):t.open(i,e.slide)});var n=$(".control-sidebar-bg");t._fix(n),$("body").hasClass("fixed")?t._fixForFixed(i):$(".content-wrapper, .right-side").height() .box-body, > .box-footer, > form >.box-body, > form > .box-footer");i.hasClass("collapsed-box")?(t.children(":first").removeClass(e.icons.open).addClass(e.icons.collapse),n.slideDown(e.animationSpeed,function(){i.removeClass("collapsed-box")})):(t.children(":first").removeClass(e.icons.collapse).addClass(e.icons.open),n.slideUp(e.animationSpeed,function(){i.addClass("collapsed-box")}))},remove:function(t){t.parents(".box").first().slideUp(this.animationSpeed)}}}if(function(t){function e(n){if(i[n])return i[n].exports;var s=i[n]={i:n,l:!1,exports:{}};return t[n].call(s.exports,s,s.exports,e),s.l=!0,s.exports}var i={};e.m=t,e.c=i,e.d=function(t,i,n){e.o(t,i)||Object.defineProperty(t,i,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=0)}({0:function(t,e,i){i("GDnL"),i("Bqz+"),i("1CH1"),t.exports=i("f5J3")},"0DKT":function(t,e){t.exports={render:function(){var t=this.$createElement;return(this._self._c||t)("select",{staticStyle:{width:"100%"}},[this._t("default")],2)},staticRenderFns:[]}},1:function(t,e){},"1CH1":function(t,e){},"1t9x":function(t,e,i){var n=i("wD+l");"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals),i("rjj0")("065bc4db",n,!0)},"20cu":function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={props:["clientsUrl","tokensUrl"],data:function(){return{tokens:[]}},ready:function(){this.prepareComponent()},mounted:function(){this.prepareComponent()},methods:{prepareComponent:function(){this.getTokens()},getTokens:function(){var t=this;this.$http.get(this.tokensUrl).then(function(e){t.tokens=e.data})},revoke:function(t){var e=this;this.$http.delete(this.tokensUrl+"/"+t.id).then(function(t){e.getTokens()})}}}},"265i":function(t,e,i){(t.exports=i("FZ+f")(void 0)).push([t.i,"",""])},"3IRH":function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},"5F58":function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),i("FHXl"),e.default={data:function(){return{files:[],displayImportModal:!1,activeFile:null,alert:{type:null,message:null,visible:!1},importErrors:null,progress:{currentClass:"progress-bar-warning",currentPercent:"0",statusText:"",visible:!1},customFields:[]}},mounted:function(){window.eventHub.$on("importErrors",this.updateImportErrors),this.fetchFiles(),this.fetchCustomFields();var t=this;$("#fileupload").fileupload({dataType:"json",done:function(e,i){t.progress.currentClass="progress-bar-success",t.progress.statusText="Success!",t.files=i.result.files.concat(t.files),console.log(i.result.header_row)},add:function(e,i){i.headers={"X-Requested-With":"XMLHttpRequest","X-CSRF-TOKEN":Laravel.csrfToken},i.process().done(function(){i.submit()}),t.progress.visible=!0},progress:function(e,i){var n=parseInt((i.loaded,i.total,10));t.progress.currentPercent=n,t.progress.statusText=n+"% Complete"},fail:function(e,i){t.progress.currentClass="progress-bar-danger",t.progress.statusText=i.jqXHR.responseJSON.messages}})},methods:{fetchFiles:function(){var t=this;this.$http.get(route("api.imports.index")).then(function(e){var i=e.data;return t.files=i},function(e){t.alert.type="danger",t.alert.visible=!0,t.alert.message="Something went wrong fetching files..."})},fetchCustomFields:function(){var t=this;this.$http.get(route("api.customfields.index")).then(function(e){var i=e.data;(i=i.rows).forEach(function(e){t.customFields.push({id:e.db_column_name,text:e.name})})})},deleteFile:function(t,e){var i=this;this.$http.delete(route("api.imports.destroy",t.id)).then(function(t){return i.files.splice(e,1)},function(t){i.alert.type="danger",i.alert.visible=!0,i.alert.message=t.body.messages})},toggleEvent:function(t){window.eventHub.$emit("showDetails",t)},updateAlert:function(t){this.alert=t},updateImportErrors:function(t){this.importErrors=t}},computed:{progressWidth:function(){return"width: "+10*this.progress.currentPercent+"%"}},components:{alert:i("b7rt"),errors:i("yDVJ"),importFile:i("sJcK")}}},"5TxM":function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("tr",{directives:[{name:"show",rawName:"v-show",value:t.processDetail,expression:"processDetail"}]},[i("td",{attrs:{colspan:"3"}},[i("h4",{staticClass:"modal-title"},[t._v("Import File:")]),t._v(" "),i("div",{staticClass:"dynamic-form-row"},[t._m(0),t._v(" "),i("div",{staticClass:"col-md-4 col-xs-12"},[i("select2",{attrs:{options:t.options.importTypes,required:""},model:{value:t.options.importType,callback:function(e){t.options.importType=e},expression:"options.importType"}},[i("option",{attrs:{disabled:"",value:"0"}})])],1)]),t._v(" "),i("div",{staticClass:"dynamic-form-row"},[t._m(1),t._v(" "),i("div",{staticClass:"col-md-4 col-xs-12"},[i("input",{directives:[{name:"model",rawName:"v-model",value:t.options.update,expression:"options.update"}],attrs:{type:"checkbox",name:"import-update"},domProps:{checked:Array.isArray(t.options.update)?t._i(t.options.update,null)>-1:t.options.update},on:{__c:function(e){var i=t.options.update,n=e.target,s=!!n.checked;if(Array.isArray(i)){var o=t._i(i,null);n.checked?o<0&&(t.options.update=i.concat([null])):o>-1&&(t.options.update=i.slice(0,o).concat(i.slice(o+1)))}else t.options.update=s}}})])]),t._v(" "),i("div",{staticClass:"col-md-12",staticStyle:{"padding-top":"30px"}},[i("table",{staticClass:"table"},[t._m(2),t._v(" "),i("tbody",[t._l(t.file.header_row,function(e,n){return[i("tr",[i("td",[i("label",{staticClass:"controllabel",attrs:{for:e}},[t._v(t._s(e))])]),t._v(" "),i("td",[i("div",{attrs:{required:""}},[i("select2",{attrs:{options:t.columns},model:{value:t.columnMappings[e],callback:function(i){t.$set(t.columnMappings,e,i)},expression:"columnMappings[header]"}},[i("option",{attrs:{value:"0"}},[t._v("Do Not Import")])])],1)]),t._v(" "),i("td",[i("div",[t._v(t._s(t.activeFile.first_row[n]))])])])]})],2)])])]),t._v(" "),i("td",[i("button",{staticClass:"btn btn-sm btn-default",attrs:{type:"button"},on:{click:function(e){t.processDetail=!1}}},[t._v("Cancel")]),t._v(" "),i("button",{staticClass:"btn btn-sm btn-primary",attrs:{type:"submit"},on:{click:t.postSave}},[t._v("Import")]),t._v(" "),t.statusText?i("div",{staticClass:"alert col-md-5 col-md-offset-1",class:t.alertClass,staticStyle:{"text-align":"left"}},[t._v("\n "+t._s(this.statusText)+"\n ")]):t._e()])])},staticRenderFns:[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"col-md-4 col-xs-12"},[e("label",{attrs:{for:"import-type"}},[this._v("Import Type:")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"col-md-4 col-xs-12"},[e("label",{attrs:{for:"import-update"}},[this._v("Update Existing Values?:")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("thead",[e("th",[this._v("Header Field")]),this._v(" "),e("th",[this._v("Import Field")]),this._v(" "),e("th",[this._v("Sample Value")])])}]}},"5m3O":function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};e.default={props:["clientsUrl"],data:function(){return{clients:[],createForm:{errors:[],name:"",redirect:""},editForm:{errors:[],name:"",redirect:""}}},ready:function(){this.prepareComponent()},mounted:function(){this.prepareComponent()},methods:{prepareComponent:function(){this.getClients(),$("#modal-create-client").on("shown.bs.modal",function(){$("#create-client-name").focus()}),$("#modal-edit-client").on("shown.bs.modal",function(){$("#edit-client-name").focus()})},getClients:function(){var t=this;this.$http.get(this.clientsUrl).then(function(e){t.clients=e.data})},showCreateClientForm:function(){$("#modal-create-client").modal("show")},store:function(){this.persistClient("post",this.clientsUrl,this.createForm,"#modal-create-client")},edit:function(t){this.editForm.id=t.id,this.editForm.name=t.name,this.editForm.redirect=t.redirect,$("#modal-edit-client").modal("show")},update:function(){this.persistClient("put",this.clientsUrl+"/"+this.editForm.id,this.editForm,"#modal-edit-client")},persistClient:function(t,e,i,s){var o=this;console.log("persisting"),i.errors=[],console.log("method: "+t),this.$http[t](e,i).then(function(t){o.getClients(),i.name="",i.redirect="",i.errors=[],$(s).modal("hide")}).catch(function(t){"object"===n(t.data)?i.errors=_.flatten(_.toArray(t.data)):i.errors=["Something went wrong. Please try again."]})},destroy:function(t){var e=this;this.$http.delete(this.clientsUrl+"/"+t.id).then(function(t){e.getClients()})}}}},"7t+N":function(t,e,i){var n;!function(e,i){"use strict";"object"==typeof t&&"object"==typeof t.exports?t.exports=e.document?i(e,!0):function(t){if(!t.document)throw new Error("jQuery requires a window with a document");return i(t)}:i(e)}("undefined"!=typeof window?window:this,function(i,s){"use strict";function o(t,e){var i=(e=e||G).createElement("script");i.text=t,e.head.appendChild(i).parentNode.removeChild(i)}function r(t){var e=!!t&&"length"in t&&t.length,i=lt.type(t);return"function"!==i&&!lt.isWindow(t)&&("array"===i||0===e||"number"==typeof e&&e>0&&e-1 in t)}function a(t,e){return t.nodeName&&t.nodeName.toLowerCase()===e.toLowerCase()}function l(t,e,i){return lt.isFunction(e)?lt.grep(t,function(t,n){return!!e.call(t,n,t)!==i}):e.nodeType?lt.grep(t,function(t){return t===e!==i}):"string"!=typeof e?lt.grep(t,function(t){return et.call(e,t)>-1!==i}):_t.test(e)?lt.filter(e,t,i):(e=lt.filter(e,t),lt.grep(t,function(t){return et.call(e,t)>-1!==i&&1===t.nodeType}))}function c(t,e){for(;(t=t[e])&&1!==t.nodeType;);return t}function u(t){return t}function h(t){throw t}function d(t,e,i,n){var s;try{t&<.isFunction(s=t.promise)?s.call(t).done(e).fail(i):t&<.isFunction(s=t.then)?s.call(t,e,i):e.apply(void 0,[t].slice(n))}catch(t){i.apply(void 0,[t])}}function p(){G.removeEventListener("DOMContentLoaded",p),i.removeEventListener("load",p),lt.ready()}function f(){this.expando=lt.expando+f.uid++}function g(t,e,i){var n,s;if(void 0===i&&1===t.nodeType)if(n="data-"+e.replace(Ot,"-$&").toLowerCase(),"string"==typeof(i=t.getAttribute(n))){try{i="true"===(s=i)||"false"!==s&&("null"===s?null:s===+s+""?+s:$t.test(s)?JSON.parse(s):s)}catch(t){}Et.set(t,e,i)}else i=void 0;return i}function m(t,e,i,n){var s,o=1,r=20,a=n?function(){return n.cur()}:function(){return lt.css(t,e,"")},l=a(),c=i&&i[3]||(lt.cssNumber[e]?"":"px"),u=(lt.cssNumber[e]||"px"!==c&&+l)&&Pt.exec(lt.css(t,e));if(u&&u[3]!==c){c=c||u[3],i=i||[],u=+l||1;do{u/=o=o||".5",lt.style(t,e,u+c)}while(o!==(o=a()/l)&&1!==o&&--r)}return i&&(u=+u||+l||0,s=i[1]?u+(i[1]+1)*i[2]:+i[2],n&&(n.unit=c,n.start=u,n.end=s)),s}function v(t,e){for(var i,n,s,o,r,a,l,c=[],u=0,h=t.length;u-1)s&&s.push(o);else if(c=lt.contains(o.ownerDocument,o),r=_(h.appendChild(o),"script"),c&&y(r),i)for(u=0;o=r[u++];)Rt.test(o.type||"")&&i.push(o);return h}function w(){return!0}function x(){return!1}function C(){try{return G.activeElement}catch(t){}}function k(t,e,i,n,s,o){var r,a;if("object"==typeof e){"string"!=typeof i&&(n=n||i,i=void 0);for(a in e)k(t,a,i,n,e[a],o);return t}if(null==n&&null==s?(s=i,n=i=void 0):null==s&&("string"==typeof i?(s=n,n=void 0):(s=n,n=i,i=void 0)),!1===s)s=x;else if(!s)return t;return 1===o&&(r=s,(s=function(t){return lt().off(t),r.apply(this,arguments)}).guid=r.guid||(r.guid=lt.guid++)),t.each(function(){lt.event.add(this,e,s,n,i)})}function D(t,e){return a(t,"table")&&a(11!==e.nodeType?e:e.firstChild,"tr")?lt(">tbody",t)[0]||t:t}function T(t){return t.type=(null!==t.getAttribute("type"))+"/"+t.type,t}function S(t){var e=Qt.exec(t.type);return e?t.type=e[1]:t.removeAttribute("type"),t}function A(t,e){var i,n,s,o,r,a,l,c;if(1===e.nodeType){if(At.hasData(t)&&(o=At.access(t),r=At.set(e,o),c=o.events)){delete r.handle,r.events={};for(s in c)for(i=0,n=c[s].length;i1&&"string"==typeof f&&!at.checkClone&&Zt.test(f))return t.each(function(s){var o=t.eq(s);g&&(e[0]=f.call(this,s,o.html())),E(o,e,i,n)});if(d&&(r=(s=b(e,t[0].ownerDocument,!1,t,n)).firstChild,1===s.childNodes.length&&(s=r),r||n)){for(l=(a=lt.map(_(s,"script"),T)).length;h=0&&iy.cacheLength&&delete e[t.shift()],e[i+" "]=n}}function n(t){return t[F]=!0,t}function s(t){var e=E.createElement("fieldset");try{return!!t(e)}catch(t){return!1}finally{e.parentNode&&e.parentNode.removeChild(e),e=null}}function o(t,e){for(var i=t.split("|"),n=i.length;n--;)y.attrHandle[i[n]]=e}function r(t,e){var i=e&&t,n=i&&1===t.nodeType&&1===e.nodeType&&t.sourceIndex-e.sourceIndex;if(n)return n;if(i)for(;i=i.nextSibling;)if(i===e)return-1;return t?1:-1}function a(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&bt(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function l(t){return n(function(e){return e=+e,n(function(i,n){for(var s,o=t([],i.length,e),r=o.length;r--;)i[s=o[r]]&&(i[s]=!(n[s]=i[s]))})})}function c(t){return t&&void 0!==t.getElementsByTagName&&t}function u(){}function h(t){for(var e=0,i=t.length,n="";e1?function(e,i,n){for(var s=t.length;s--;)if(!t[s](e,i,n))return!1;return!0}:t[0]}function f(t,e,i,n,s){for(var o,r=[],a=0,l=t.length,c=null!=e;a-1&&(n[u]=!(a[u]=d))}}else y=f(y===a?y.splice(m,y.length):y),r?r(null,a,y,c):X.apply(a,y)})}function m(t){for(var e,i,n,s=t.length,o=y.relative[t[0].type],r=o||y.relative[" "],a=o?1:0,l=d(function(t){return t===e},r,!0),c=d(function(t){return G(e,t)>-1},r,!0),u=[function(t,i,n){var s=!o&&(n||i!==D)||((e=i).nodeType?l(t,i,n):c(t,i,n));return e=null,s}];a1&&p(u),a>1&&h(t.slice(0,a-1).concat({value:" "===t[a-2].type?"*":""})).replace(nt,"$1"),i,a+~]|"+Q+")"+Q+"*"),rt=new RegExp("="+Q+"*([^\\]'\"]*?)"+Q+"*\\]","g"),at=new RegExp(et),lt=new RegExp("^"+J+"$"),ct={ID:new RegExp("^#("+J+")"),CLASS:new RegExp("^\\.("+J+")"),TAG:new RegExp("^("+J+"|[*])"),ATTR:new RegExp("^"+tt),PSEUDO:new RegExp("^"+et),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+Q+"*(even|odd|(([+-]|)(\\d*)n|)"+Q+"*(?:([+-]|)"+Q+"*(\\d+)|))"+Q+"*\\)|)","i"),bool:new RegExp("^(?:"+Z+")$","i"),needsContext:new RegExp("^"+Q+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+Q+"*((?:-\\d)?\\d*)"+Q+"*\\)|)(?=[^-]|$)","i")},ut=/^(?:input|select|textarea|button)$/i,ht=/^h\d$/i,dt=/^[^{]+\{\s*\[native \w/,pt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ft=/[+~]/,gt=new RegExp("\\\\([\\da-f]{1,6}"+Q+"?|("+Q+")|.)","ig"),mt=function(t,e,i){var n="0x"+e-65536;return n!=n||i?e:n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320)},vt=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,_t=function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t},yt=function(){A()},bt=d(function(t){return!0===t.disabled&&("form"in t||"label"in t)},{dir:"parentNode",next:"legend"});try{X.apply(q=K.call(j.childNodes),j.childNodes),q[j.childNodes.length].nodeType}catch(t){X={apply:q.length?function(t,e){Y.apply(t,K.call(e))}:function(t,e){for(var i=t.length,n=0;t[i++]=e[n++];);t.length=i-1}}}_=e.support={},w=e.isXML=function(t){var e=t&&(t.ownerDocument||t).documentElement;return!!e&&"HTML"!==e.nodeName},A=e.setDocument=function(t){var e,i,n=t?t.ownerDocument||t:j;return n!==E&&9===n.nodeType&&n.documentElement?($=(E=n).documentElement,O=!w(E),j!==E&&(i=E.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",yt,!1):i.attachEvent&&i.attachEvent("onunload",yt)),_.attributes=s(function(t){return t.className="i",!t.getAttribute("className")}),_.getElementsByTagName=s(function(t){return t.appendChild(E.createComment("")),!t.getElementsByTagName("*").length}),_.getElementsByClassName=dt.test(E.getElementsByClassName),_.getById=s(function(t){return $.appendChild(t).id=F,!E.getElementsByName||!E.getElementsByName(F).length}),_.getById?(y.filter.ID=function(t){var e=t.replace(gt,mt);return function(t){return t.getAttribute("id")===e}},y.find.ID=function(t,e){if(void 0!==e.getElementById&&O){var i=e.getElementById(t);return i?[i]:[]}}):(y.filter.ID=function(t){var e=t.replace(gt,mt);return function(t){var i=void 0!==t.getAttributeNode&&t.getAttributeNode("id");return i&&i.value===e}},y.find.ID=function(t,e){if(void 0!==e.getElementById&&O){var i,n,s,o=e.getElementById(t);if(o){if((i=o.getAttributeNode("id"))&&i.value===t)return[o];for(s=e.getElementsByName(t),n=0;o=s[n++];)if((i=o.getAttributeNode("id"))&&i.value===t)return[o]}return[]}}),y.find.TAG=_.getElementsByTagName?function(t,e){return void 0!==e.getElementsByTagName?e.getElementsByTagName(t):_.qsa?e.querySelectorAll(t):void 0}:function(t,e){var i,n=[],s=0,o=e.getElementsByTagName(t);if("*"===t){for(;i=o[s++];)1===i.nodeType&&n.push(i);return n}return o},y.find.CLASS=_.getElementsByClassName&&function(t,e){if(void 0!==e.getElementsByClassName&&O)return e.getElementsByClassName(t)},P=[],I=[],(_.qsa=dt.test(E.querySelectorAll))&&(s(function(t){$.appendChild(t).innerHTML="",t.querySelectorAll("[msallowcapture^='']").length&&I.push("[*^$]="+Q+"*(?:''|\"\")"),t.querySelectorAll("[selected]").length||I.push("\\["+Q+"*(?:value|"+Z+")"),t.querySelectorAll("[id~="+F+"-]").length||I.push("~="),t.querySelectorAll(":checked").length||I.push(":checked"),t.querySelectorAll("a#"+F+"+*").length||I.push(".#.+[+~]")}),s(function(t){t.innerHTML="";var e=E.createElement("input");e.setAttribute("type","hidden"),t.appendChild(e).setAttribute("name","D"),t.querySelectorAll("[name=d]").length&&I.push("name"+Q+"*[*^$|!~]?="),2!==t.querySelectorAll(":enabled").length&&I.push(":enabled",":disabled"),$.appendChild(t).disabled=!0,2!==t.querySelectorAll(":disabled").length&&I.push(":enabled",":disabled"),t.querySelectorAll("*,:x"),I.push(",.*:")})),(_.matchesSelector=dt.test(M=$.matches||$.webkitMatchesSelector||$.mozMatchesSelector||$.oMatchesSelector||$.msMatchesSelector))&&s(function(t){_.disconnectedMatch=M.call(t,"*"),M.call(t,"[s!='']:x"),P.push("!=",et)}),I=I.length&&new RegExp(I.join("|")),P=P.length&&new RegExp(P.join("|")),e=dt.test($.compareDocumentPosition),N=e||dt.test($.contains)?function(t,e){var i=9===t.nodeType?t.documentElement:t,n=e&&e.parentNode;return t===n||!(!n||1!==n.nodeType||!(i.contains?i.contains(n):t.compareDocumentPosition&&16&t.compareDocumentPosition(n)))}:function(t,e){if(e)for(;e=e.parentNode;)if(e===t)return!0;return!1},U=e?function(t,e){if(t===e)return S=!0,0;var i=!t.compareDocumentPosition-!e.compareDocumentPosition;return i||(1&(i=(t.ownerDocument||t)===(e.ownerDocument||e)?t.compareDocumentPosition(e):1)||!_.sortDetached&&e.compareDocumentPosition(t)===i?t===E||t.ownerDocument===j&&N(j,t)?-1:e===E||e.ownerDocument===j&&N(j,e)?1:T?G(T,t)-G(T,e):0:4&i?-1:1)}:function(t,e){if(t===e)return S=!0,0;var i,n=0,s=t.parentNode,o=e.parentNode,a=[t],l=[e];if(!s||!o)return t===E?-1:e===E?1:s?-1:o?1:T?G(T,t)-G(T,e):0;if(s===o)return r(t,e);for(i=t;i=i.parentNode;)a.unshift(i);for(i=e;i=i.parentNode;)l.unshift(i);for(;a[n]===l[n];)n++;return n?r(a[n],l[n]):a[n]===j?-1:l[n]===j?1:0},E):E},e.matches=function(t,i){return e(t,null,null,i)},e.matchesSelector=function(t,i){if((t.ownerDocument||t)!==E&&A(t),i=i.replace(rt,"='$1']"),_.matchesSelector&&O&&!W[i+" "]&&(!P||!P.test(i))&&(!I||!I.test(i)))try{var n=M.call(t,i);if(n||_.disconnectedMatch||t.document&&11!==t.document.nodeType)return n}catch(t){}return e(i,E,null,[t]).length>0},e.contains=function(t,e){return(t.ownerDocument||t)!==E&&A(t),N(t,e)},e.attr=function(t,e){(t.ownerDocument||t)!==E&&A(t);var i=y.attrHandle[e.toLowerCase()],n=i&&B.call(y.attrHandle,e.toLowerCase())?i(t,e,!O):void 0;return void 0!==n?n:_.attributes||!O?t.getAttribute(e):(n=t.getAttributeNode(e))&&n.specified?n.value:null},e.escape=function(t){return(t+"").replace(vt,_t)},e.error=function(t){throw new Error("Syntax error, unrecognized expression: "+t)},e.uniqueSort=function(t){var e,i=[],n=0,s=0;if(S=!_.detectDuplicates,T=!_.sortStable&&t.slice(0),t.sort(U),S){for(;e=t[s++];)e===t[s]&&(n=i.push(s));for(;n--;)t.splice(i[n],1)}return T=null,t},b=e.getText=function(t){var e,i="",n=0,s=t.nodeType;if(s){if(1===s||9===s||11===s){if("string"==typeof t.textContent)return t.textContent;for(t=t.firstChild;t;t=t.nextSibling)i+=b(t)}else if(3===s||4===s)return t.nodeValue}else for(;e=t[n++];)i+=b(e);return i},(y=e.selectors={cacheLength:50,createPseudo:n,match:ct,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(t){return t[1]=t[1].replace(gt,mt),t[3]=(t[3]||t[4]||t[5]||"").replace(gt,mt),"~="===t[2]&&(t[3]=" "+t[3]+" "),t.slice(0,4)},CHILD:function(t){return t[1]=t[1].toLowerCase(),"nth"===t[1].slice(0,3)?(t[3]||e.error(t[0]),t[4]=+(t[4]?t[5]+(t[6]||1):2*("even"===t[3]||"odd"===t[3])),t[5]=+(t[7]+t[8]||"odd"===t[3])):t[3]&&e.error(t[0]),t},PSEUDO:function(t){var e,i=!t[6]&&t[2];return ct.CHILD.test(t[0])?null:(t[3]?t[2]=t[4]||t[5]||"":i&&at.test(i)&&(e=x(i,!0))&&(e=i.indexOf(")",i.length-e)-i.length)&&(t[0]=t[0].slice(0,e),t[2]=i.slice(0,e)),t.slice(0,3))}},filter:{TAG:function(t){var e=t.replace(gt,mt).toLowerCase();return"*"===t?function(){return!0}:function(t){return t.nodeName&&t.nodeName.toLowerCase()===e}},CLASS:function(t){var e=R[t+" "];return e||(e=new RegExp("(^|"+Q+")"+t+"("+Q+"|$)"))&&R(t,function(t){return e.test("string"==typeof t.className&&t.className||void 0!==t.getAttribute&&t.getAttribute("class")||"")})},ATTR:function(t,i,n){return function(s){var o=e.attr(s,t);return null==o?"!="===i:!i||(o+="","="===i?o===n:"!="===i?o!==n:"^="===i?n&&0===o.indexOf(n):"*="===i?n&&o.indexOf(n)>-1:"$="===i?n&&o.slice(-n.length)===n:"~="===i?(" "+o.replace(it," ")+" ").indexOf(n)>-1:"|="===i&&(o===n||o.slice(0,n.length+1)===n+"-"))}},CHILD:function(t,e,i,n,s){var o="nth"!==t.slice(0,3),r="last"!==t.slice(-4),a="of-type"===e;return 1===n&&0===s?function(t){return!!t.parentNode}:function(e,i,l){var c,u,h,d,p,f,g=o!==r?"nextSibling":"previousSibling",m=e.parentNode,v=a&&e.nodeName.toLowerCase(),_=!l&&!a,y=!1;if(m){if(o){for(;g;){for(d=e;d=d[g];)if(a?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;f=g="only"===t&&!f&&"nextSibling"}return!0}if(f=[r?m.firstChild:m.lastChild],r&&_){for(y=(p=(c=(u=(h=(d=m)[F]||(d[F]={}))[d.uniqueID]||(h[d.uniqueID]={}))[t]||[])[0]===H&&c[1])&&c[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(y=p=0)||f.pop();)if(1===d.nodeType&&++y&&d===e){u[t]=[H,p,y];break}}else if(_&&(y=p=(c=(u=(h=(d=e)[F]||(d[F]={}))[d.uniqueID]||(h[d.uniqueID]={}))[t]||[])[0]===H&&c[1]),!1===y)for(;(d=++p&&d&&d[g]||(y=p=0)||f.pop())&&((a?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++y||(_&&((u=(h=d[F]||(d[F]={}))[d.uniqueID]||(h[d.uniqueID]={}))[t]=[H,y]),d!==e)););return(y-=s)===n||y%n==0&&y/n>=0}}},PSEUDO:function(t,i){var s,o=y.pseudos[t]||y.setFilters[t.toLowerCase()]||e.error("unsupported pseudo: "+t);return o[F]?o(i):o.length>1?(s=[t,t,"",i],y.setFilters.hasOwnProperty(t.toLowerCase())?n(function(t,e){for(var n,s=o(t,i),r=s.length;r--;)t[n=G(t,s[r])]=!(e[n]=s[r])}):function(t){return o(t,0,s)}):o}},pseudos:{not:n(function(t){var e=[],i=[],s=C(t.replace(nt,"$1"));return s[F]?n(function(t,e,i,n){for(var o,r=s(t,null,n,[]),a=t.length;a--;)(o=r[a])&&(t[a]=!(e[a]=o))}):function(t,n,o){return e[0]=t,s(e,null,o,i),e[0]=null,!i.pop()}}),has:n(function(t){return function(i){return e(t,i).length>0}}),contains:n(function(t){return t=t.replace(gt,mt),function(e){return(e.textContent||e.innerText||b(e)).indexOf(t)>-1}}),lang:n(function(t){return lt.test(t||"")||e.error("unsupported lang: "+t),t=t.replace(gt,mt).toLowerCase(),function(e){var i;do{if(i=O?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(i=i.toLowerCase())===t||0===i.indexOf(t+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var i=t.location&&t.location.hash;return i&&i.slice(1)===e.id},root:function(t){return t===$},focus:function(t){return t===E.activeElement&&(!E.hasFocus||E.hasFocus())&&!!(t.type||t.href||~t.tabIndex)},enabled:a(!1),disabled:a(!0),checked:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&!!t.checked||"option"===e&&!!t.selected},selected:function(t){return t.parentNode&&t.parentNode.selectedIndex,!0===t.selected},empty:function(t){for(t=t.firstChild;t;t=t.nextSibling)if(t.nodeType<6)return!1;return!0},parent:function(t){return!y.pseudos.empty(t)},header:function(t){return ht.test(t.nodeName)},input:function(t){return ut.test(t.nodeName)},button:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&"button"===t.type||"button"===e},text:function(t){var e;return"input"===t.nodeName.toLowerCase()&&"text"===t.type&&(null==(e=t.getAttribute("type"))||"text"===e.toLowerCase())},first:l(function(){return[0]}),last:l(function(t,e){return[e-1]}),eq:l(function(t,e,i){return[i<0?i+e:i]}),even:l(function(t,e){for(var i=0;i=0;)t.push(n);return t}),gt:l(function(t,e,i){for(var n=i<0?i+e:i;++n0,l=o.length>0,c=function(t,i,n,s,c){var u,h,d,p=0,g="0",m=t&&[],v=[],_=D,b=t||l&&y.find.TAG("*",c),w=H+=null==_?1:Math.random()||.1,x=b.length;for(c&&(D=i===E||i||c);g!==x&&null!=(u=b[g]);g++){if(l&&u){for(h=0,i||u.ownerDocument===E||(A(u),n=!O);d=o[h++];)if(d(u,i||E,n)){s.push(u);break}c&&(H=w)}a&&((u=!d&&u)&&p--,t&&m.push(u))}if(p+=g,a&&g!==p){for(h=0;d=r[h++];)d(m,v,i,n);if(t){if(p>0)for(;g--;)m[g]||v[g]||(v[g]=V.call(s));v=f(v)}X.apply(s,v),c&&!t&&v.length>0&&p+r.length>1&&e.uniqueSort(s)}return c&&(H=w,D=_),m},a?n(c):c))).selector=t}return d},k=e.select=function(t,e,i,n){var s,o,r,a,l,u="function"==typeof t&&t,d=!n&&x(t=u.selector||t);if(i=i||[],1===d.length){if((o=d[0]=d[0].slice(0)).length>2&&"ID"===(r=o[0]).type&&9===e.nodeType&&O&&y.relative[o[1].type]){if(!(e=(y.find.ID(r.matches[0].replace(gt,mt),e)||[])[0]))return i;u&&(e=e.parentNode),t=t.slice(o.shift().value.length)}for(s=ct.needsContext.test(t)?0:o.length;s--&&(r=o[s],!y.relative[a=r.type]);)if((l=y.find[a])&&(n=l(r.matches[0].replace(gt,mt),ft.test(o[0].type)&&c(e.parentNode)||e))){if(o.splice(s,1),!(t=n.length&&h(o)))return X.apply(i,n),i;break}}return(u||C(t,d))(n,e,!O,i,!e||ft.test(t)&&c(e.parentNode)||e),i},_.sortStable=F.split("").sort(U).join("")===F,_.detectDuplicates=!!S,A(),_.sortDetached=s(function(t){return 1&t.compareDocumentPosition(E.createElement("fieldset"))}),s(function(t){return t.innerHTML="","#"===t.firstChild.getAttribute("href")})||o("type|href|height|width",function(t,e,i){if(!i)return t.getAttribute(e,"type"===e.toLowerCase()?1:2)}),_.attributes&&s(function(t){return t.innerHTML="",t.firstChild.setAttribute("value",""),""===t.firstChild.getAttribute("value")})||o("value",function(t,e,i){if(!i&&"input"===t.nodeName.toLowerCase())return t.defaultValue}),s(function(t){return null==t.getAttribute("disabled")})||o(Z,function(t,e,i){var n;if(!i)return!0===t[e]?e.toLowerCase():(n=t.getAttributeNode(e))&&n.specified?n.value:null}),e}(i);lt.find=pt,lt.expr=pt.selectors,lt.expr[":"]=lt.expr.pseudos,lt.uniqueSort=lt.unique=pt.uniqueSort,lt.text=pt.getText,lt.isXMLDoc=pt.isXML,lt.contains=pt.contains,lt.escapeSelector=pt.escape;var ft=function(t,e,i){for(var n=[],s=void 0!==i;(t=t[e])&&9!==t.nodeType;)if(1===t.nodeType){if(s&<(t).is(i))break;n.push(t)}return n},gt=function(t,e){for(var i=[];t;t=t.nextSibling)1===t.nodeType&&t!==e&&i.push(t);return i},mt=lt.expr.match.needsContext,vt=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,_t=/^.[^:#\[\.,]*$/;lt.filter=function(t,e,i){var n=e[0];return i&&(t=":not("+t+")"),1===e.length&&1===n.nodeType?lt.find.matchesSelector(n,t)?[n]:[]:lt.find.matches(t,lt.grep(e,function(t){return 1===t.nodeType}))},lt.fn.extend({find:function(t){var e,i,n=this.length,s=this;if("string"!=typeof t)return this.pushStack(lt(t).filter(function(){for(e=0;e1?lt.uniqueSort(i):i},filter:function(t){return this.pushStack(l(this,t||[],!1))},not:function(t){return this.pushStack(l(this,t||[],!0))},is:function(t){return!!l(this,"string"==typeof t&&mt.test(t)?lt(t):t||[],!1).length}});var yt,bt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(lt.fn.init=function(t,e,i){var n,s;if(!t)return this;if(i=i||yt,"string"==typeof t){if(!(n="<"===t[0]&&">"===t[t.length-1]&&t.length>=3?[null,t,null]:bt.exec(t))||!n[1]&&e)return!e||e.jquery?(e||i).find(t):this.constructor(e).find(t);if(n[1]){if(e=e instanceof lt?e[0]:e,lt.merge(this,lt.parseHTML(n[1],e&&e.nodeType?e.ownerDocument||e:G,!0)),vt.test(n[1])&<.isPlainObject(e))for(n in e)lt.isFunction(this[n])?this[n](e[n]):this.attr(n,e[n]);return this}return(s=G.getElementById(n[2]))&&(this[0]=s,this.length=1),this}return t.nodeType?(this[0]=t,this.length=1,this):lt.isFunction(t)?void 0!==i.ready?i.ready(t):t(lt):lt.makeArray(t,this)}).prototype=lt.fn,yt=lt(G);var wt=/^(?:parents|prev(?:Until|All))/,xt={children:!0,contents:!0,next:!0,prev:!0};lt.fn.extend({has:function(t){var e=lt(t,this),i=e.length;return this.filter(function(){for(var t=0;t-1:1===i.nodeType&<.find.matchesSelector(i,t))){o.push(i);break}return this.pushStack(o.length>1?lt.uniqueSort(o):o)},index:function(t){return t?"string"==typeof t?et.call(lt(t),this[0]):et.call(this,t.jquery?t[0]:t):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(t,e){return this.pushStack(lt.uniqueSort(lt.merge(this.get(),lt(t,e))))},addBack:function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}}),lt.each({parent:function(t){var e=t.parentNode;return e&&11!==e.nodeType?e:null},parents:function(t){return ft(t,"parentNode")},parentsUntil:function(t,e,i){return ft(t,"parentNode",i)},next:function(t){return c(t,"nextSibling")},prev:function(t){return c(t,"previousSibling")},nextAll:function(t){return ft(t,"nextSibling")},prevAll:function(t){return ft(t,"previousSibling")},nextUntil:function(t,e,i){return ft(t,"nextSibling",i)},prevUntil:function(t,e,i){return ft(t,"previousSibling",i)},siblings:function(t){return gt((t.parentNode||{}).firstChild,t)},children:function(t){return gt(t.firstChild)},contents:function(t){return a(t,"iframe")?t.contentDocument:(a(t,"template")&&(t=t.content||t),lt.merge([],t.childNodes))}},function(t,e){lt.fn[t]=function(i,n){var s=lt.map(this,e,i);return"Until"!==t.slice(-5)&&(n=i),n&&"string"==typeof n&&(s=lt.filter(n,s)),this.length>1&&(xt[t]||lt.uniqueSort(s),wt.test(t)&&s.reverse()),this.pushStack(s)}});var Ct=/[^\x20\t\r\n\f]+/g;lt.Callbacks=function(t){var e,i;t="string"==typeof t?(e=t,i={},lt.each(e.match(Ct)||[],function(t,e){i[e]=!0}),i):lt.extend({},t);var n,s,o,r,a=[],l=[],c=-1,u=function(){for(r=r||t.once,o=n=!0;l.length;c=-1)for(s=l.shift();++c-1;)a.splice(i,1),i<=c&&c--}),this},has:function(t){return t?lt.inArray(t,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return r=l=[],a=s="",this},disabled:function(){return!a},lock:function(){return r=l=[],s||n||(a=s=""),this},locked:function(){return!!r},fireWith:function(t,e){return r||(e=[t,(e=e||[]).slice?e.slice():e],l.push(e),n||u()),this},fire:function(){return h.fireWith(this,arguments),this},fired:function(){return!!o}};return h},lt.extend({Deferred:function(t){var e=[["notify","progress",lt.Callbacks("memory"),lt.Callbacks("memory"),2],["resolve","done",lt.Callbacks("once memory"),lt.Callbacks("once memory"),0,"resolved"],["reject","fail",lt.Callbacks("once memory"),lt.Callbacks("once memory"),1,"rejected"]],n="pending",s={state:function(){return n},always:function(){return o.done(arguments).fail(arguments),this},catch:function(t){return s.then(null,t)},pipe:function(){var t=arguments;return lt.Deferred(function(i){lt.each(e,function(e,n){var s=lt.isFunction(t[n[4]])&&t[n[4]];o[n[1]](function(){var t=s&&s.apply(this,arguments);t&<.isFunction(t.promise)?t.promise().progress(i.notify).done(i.resolve).fail(i.reject):i[n[0]+"With"](this,s?[t]:arguments)})}),t=null}).promise()},then:function(t,n,s){function o(t,e,n,s){return function(){var a=this,l=arguments,c=function(){var i,c;if(!(t=r&&(n!==h&&(a=void 0,l=[i]),e.rejectWith(a,l))}};t?d():(lt.Deferred.getStackHook&&(d.stackTrace=lt.Deferred.getStackHook()),i.setTimeout(d))}}var r=0;return lt.Deferred(function(i){e[0][3].add(o(0,i,lt.isFunction(s)?s:u,i.notifyWith)),e[1][3].add(o(0,i,lt.isFunction(t)?t:u)),e[2][3].add(o(0,i,lt.isFunction(n)?n:h))}).promise()},promise:function(t){return null!=t?lt.extend(t,s):s}},o={};return lt.each(e,function(t,i){var r=i[2],a=i[5];s[i[1]]=r.add,a&&r.add(function(){n=a},e[3-t][2].disable,e[0][2].lock),r.add(i[3].fire),o[i[0]]=function(){return o[i[0]+"With"](this===o?void 0:this,arguments),this},o[i[0]+"With"]=r.fireWith}),s.promise(o),t&&t.call(o,o),o},when:function(t){var e=arguments.length,i=e,n=Array(i),s=Q.call(arguments),o=lt.Deferred(),r=function(t){return function(i){n[t]=this,s[t]=arguments.length>1?Q.call(arguments):i,--e||o.resolveWith(n,s)}};if(e<=1&&(d(t,o.done(r(i)).resolve,o.reject,!e),"pending"===o.state()||lt.isFunction(s[i]&&s[i].then)))return o.then();for(;i--;)d(s[i],r(i),o.reject);return o.promise()}});var kt=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;lt.Deferred.exceptionHook=function(t,e){i.console&&i.console.warn&&t&&kt.test(t.name)&&i.console.warn("jQuery.Deferred exception: "+t.message,t.stack,e)},lt.readyException=function(t){i.setTimeout(function(){throw t})};var Dt=lt.Deferred();lt.fn.ready=function(t){return Dt.then(t).catch(function(t){lt.readyException(t)}),this},lt.extend({isReady:!1,readyWait:1,ready:function(t){(!0===t?--lt.readyWait:lt.isReady)||(lt.isReady=!0,!0!==t&&--lt.readyWait>0||Dt.resolveWith(G,[lt]))}}),lt.ready.then=Dt.then,"complete"===G.readyState||"loading"!==G.readyState&&!G.documentElement.doScroll?i.setTimeout(lt.ready):(G.addEventListener("DOMContentLoaded",p),i.addEventListener("load",p));var Tt=function(t,e,i,n,s,o,r){var a=0,l=t.length,c=null==i;if("object"===lt.type(i)){s=!0;for(a in i)Tt(t,e,a,i[a],!0,o,r)}else if(void 0!==n&&(s=!0,lt.isFunction(n)||(r=!0),c&&(r?(e.call(t,n),e=null):(c=e,e=function(t,e,i){return c.call(lt(t),i)})),e))for(;a1,null,!0)},removeData:function(t){return this.each(function(){Et.remove(this,t)})}}),lt.extend({queue:function(t,e,i){var n;if(t)return e=(e||"fx")+"queue",n=At.get(t,e),i&&(!n||Array.isArray(i)?n=At.access(t,e,lt.makeArray(i)):n.push(i)),n||[]},dequeue:function(t,e){e=e||"fx";var i=lt.queue(t,e),n=i.length,s=i.shift(),o=lt._queueHooks(t,e);"inprogress"===s&&(s=i.shift(),n--),s&&("fx"===e&&i.unshift("inprogress"),delete o.stop,s.call(t,function(){lt.dequeue(t,e)},o)),!n&&o&&o.empty.fire()},_queueHooks:function(t,e){var i=e+"queueHooks";return At.get(t,i)||At.access(t,i,{empty:lt.Callbacks("once memory").add(function(){At.remove(t,[e+"queue",i])})})}}),lt.fn.extend({queue:function(t,e){var i=2;return"string"!=typeof t&&(e=t,t="fx",i--),arguments.length\x20\t\r\n\f]+)/i,Rt=/^$|\/(?:java|ecma)script/i,zt={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};zt.optgroup=zt.option,zt.tbody=zt.tfoot=zt.colgroup=zt.caption=zt.thead,zt.th=zt.td;var Wt,Ut,Bt=/<|?\w+;/;Wt=G.createDocumentFragment().appendChild(G.createElement("div")),(Ut=G.createElement("input")).setAttribute("type","radio"),Ut.setAttribute("checked","checked"),Ut.setAttribute("name","t"),Wt.appendChild(Ut),at.checkClone=Wt.cloneNode(!0).cloneNode(!0).lastChild.checked,Wt.innerHTML="",at.noCloneChecked=!!Wt.cloneNode(!0).lastChild.defaultValue;var qt=G.documentElement,Vt=/^key/,Yt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Xt=/^([^.]*)(?:\.(.+)|)/;lt.event={global:{},add:function(t,e,i,n,s){var o,r,a,l,c,u,h,d,p,f,g,m=At.get(t);if(m)for(i.handler&&(i=(o=i).handler,s=o.selector),s&<.find.matchesSelector(qt,s),i.guid||(i.guid=lt.guid++),(l=m.events)||(l=m.events={}),(r=m.handle)||(r=m.handle=function(e){return void 0!==lt&<.event.triggered!==e.type?lt.event.dispatch.apply(t,arguments):void 0}),c=(e=(e||"").match(Ct)||[""]).length;c--;)p=g=(a=Xt.exec(e[c])||[])[1],f=(a[2]||"").split(".").sort(),p&&(h=lt.event.special[p]||{},p=(s?h.delegateType:h.bindType)||p,h=lt.event.special[p]||{},u=lt.extend({type:p,origType:g,data:n,handler:i,guid:i.guid,selector:s,needsContext:s&<.expr.match.needsContext.test(s),namespace:f.join(".")},o),(d=l[p])||((d=l[p]=[]).delegateCount=0,h.setup&&!1!==h.setup.call(t,n,f,r)||t.addEventListener&&t.addEventListener(p,r)),h.add&&(h.add.call(t,u),u.handler.guid||(u.handler.guid=i.guid)),s?d.splice(d.delegateCount++,0,u):d.push(u),lt.event.global[p]=!0)},remove:function(t,e,i,n,s){var o,r,a,l,c,u,h,d,p,f,g,m=At.hasData(t)&&At.get(t);if(m&&(l=m.events)){for(c=(e=(e||"").match(Ct)||[""]).length;c--;)if(p=g=(a=Xt.exec(e[c])||[])[1],f=(a[2]||"").split(".").sort(),p){for(h=lt.event.special[p]||{},d=l[p=(n?h.delegateType:h.bindType)||p]||[],a=a[2]&&new RegExp("(^|\\.)"+f.join("\\.(?:.*\\.|)")+"(\\.|$)"),r=o=d.length;o--;)u=d[o],!s&&g!==u.origType||i&&i.guid!==u.guid||a&&!a.test(u.namespace)||n&&n!==u.selector&&("**"!==n||!u.selector)||(d.splice(o,1),u.selector&&d.delegateCount--,h.remove&&h.remove.call(t,u));r&&!d.length&&(h.teardown&&!1!==h.teardown.call(t,f,m.handle)||lt.removeEvent(t,p,m.handle),delete l[p])}else for(p in l)lt.event.remove(t,p+e[c],i,n,!0);lt.isEmptyObject(l)&&At.remove(t,"handle events")}},dispatch:function(t){var e,i,n,s,o,r,a=lt.event.fix(t),l=new Array(arguments.length),c=(At.get(this,"events")||{})[a.type]||[],u=lt.event.special[a.type]||{};for(l[0]=a,e=1;e=1))for(;c!==this;c=c.parentNode||this)if(1===c.nodeType&&("click"!==t.type||!0!==c.disabled)){for(o=[],r={},i=0;i-1:lt.find(s,this,null,[c]).length),r[s]&&o.push(n);o.length&&a.push({elem:c,handlers:o})}return c=this,l\x20\t\r\n\f]*)[^>]*)\/>/gi,Gt=/