mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-10 07:34:06 -08:00
Merge branch 'develop' into user_setup_translation
This commit is contained in:
commit
29851c626f
|
@ -20,13 +20,13 @@ PUBLIC_FILESYSTEM_DISK=local_public
|
|||
# REQUIRED: DATABASE SETTINGS
|
||||
# --------------------------------------------
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=snipeit-local
|
||||
DB_USERNAME=snipeit-local
|
||||
DB_PASSWORD=snipeit-local
|
||||
DB_DATABASE=null
|
||||
DB_USERNAME=null
|
||||
DB_PASSWORD=null
|
||||
DB_PREFIX=null
|
||||
DB_DUMP_PATH='/Applications/MAMP/Library/bin'
|
||||
#DB_DUMP_PATH=
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SSL DATABASE SETTINGS
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
.couscous
|
||||
.DS_Store
|
||||
.env
|
||||
.env.dusk.*
|
||||
!.env.dusk.example
|
||||
.idea
|
||||
/bin/
|
||||
/bootstrap/compiled.php
|
||||
|
|
24
TESTING.md
24
TESTING.md
|
@ -43,23 +43,33 @@ you want to run.
|
|||
|
||||
## Browser Tests
|
||||
|
||||
The browser tests use [Dusk](https://laravel.com/docs/8.x/dusk) to run them.
|
||||
When troubleshooting any problems, make sure that your `.env` file is configured
|
||||
correctly to run the existing application.
|
||||
Browser tests are run via [Laravel Dusk](https://laravel.com/docs/8.x/dusk) and require Google Chrome to be installed.
|
||||
|
||||
Before attempting to run Dusk tests copy the example environment file for Dusk and update the values to match your environment:
|
||||
|
||||
`cp .env.dusk.example .env.dusk.local`
|
||||
> `local` refers to the value of `APP_ENV` in your `.env` so if you have it set to `dev` then the file should be named `.env.dusk.dev`.
|
||||
|
||||
**Important**: Dusk tests cannot be run using an in-memory SQLite database. Additionally, the Dusk test suite uses the `DatabaseMigrations` trait which will leave the database in a fresh state after running. Therefore, it is recommended that you create a test database and point `DB_DATABASE` in `.env.dusk.local` to it.
|
||||
|
||||
### Test Setup
|
||||
|
||||
Your application needs to be configued and up and running in order for the browser
|
||||
Your application needs to be configured and up and running in order for the browser
|
||||
tests to actually run. When running the tests locally, you can start the application
|
||||
using the following command:
|
||||
|
||||
`php artisan serve`
|
||||
|
||||
|
||||
To run the test suite use the following command from another terminal tab or window:
|
||||
Now you are ready to run the test suite. Use the following command from another terminal tab or window:
|
||||
|
||||
`php artisan dusk`
|
||||
|
||||
To run individual test files, you can pass the path to the test that you want to run.
|
||||
To run individual test files, you can pass the path to the test that you want to run:
|
||||
|
||||
`php artisan dusk tests/Browser/LoginTest.php`
|
||||
|
||||
If you get an error when attempting to run Dusk tests that says `Couldn't connect to server` run:
|
||||
|
||||
`php artisan dusk:chrome-driver --detect`
|
||||
|
||||
This command will install the specific ChromeDriver Dusk needs for your operating system and Chrome version.
|
||||
|
|
|
@ -44,12 +44,18 @@ class LdapSync extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
// If LDAP enabled isn't set to 1 (ldap_enabled!=1) then we should cut this short immediately without going any further
|
||||
if (Setting::getSettings()->ldap_enabled!='1') {
|
||||
$this->error('LDAP is not enabled. Aborting. See Settings > LDAP to enable it.');
|
||||
exit();
|
||||
}
|
||||
|
||||
ini_set('max_execution_time', env('LDAP_TIME_LIM', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('LDAP_MEM_LIM', '500M'));
|
||||
$ldap_result_username = Setting::getSettings()->ldap_username_field;
|
||||
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
|
||||
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
|
||||
|
||||
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag;
|
||||
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
|
||||
$ldap_result_email = Setting::getSettings()->ldap_email;
|
||||
|
@ -303,17 +309,18 @@ class LdapSync extends Command
|
|||
$user->activated = 0;
|
||||
} */
|
||||
$enabled_accounts = [
|
||||
'512', // 0x200 NORMAL_ACCOUNT
|
||||
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
|
||||
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
|
||||
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
|
||||
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
|
||||
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
|
||||
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'4194816',// 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH
|
||||
'512', // 0x200 NORMAL_ACCOUNT
|
||||
'544', // 0x220 NORMAL_ACCOUNT, PASSWD_NOTREQD
|
||||
'66048', // 0x10200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
|
||||
'66080', // 0x10220 NORMAL_ACCOUNT, PASSWD_NOTREQD, DONT_EXPIRE_PASSWORD
|
||||
'262656', // 0x40200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED
|
||||
'262688', // 0x40220 NORMAL_ACCOUNT, PASSWD_NOTREQD, SMARTCARD_REQUIRED
|
||||
'328192', // 0x50200 NORMAL_ACCOUNT, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'328224', // 0x50220 NORMAL_ACCOUNT, PASSWD_NOT_REQD, SMARTCARD_REQUIRED, DONT_EXPIRE_PASSWORD
|
||||
'4194816',// 0x400200 NORMAL_ACCOUNT, DONT_REQ_PREAUTH
|
||||
'4260352', // 0x410200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, DONT_REQ_PREAUTH
|
||||
'1049088', // 0x100200 NORMAL_ACCOUNT, NOT_DELEGATED
|
||||
'1114624', // 0x110200 NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD, NOT_DELEGATED,
|
||||
];
|
||||
$user->activated = (in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts)) ? 1 : 0;
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ class RestoreFromBackup extends Command
|
|||
$boring_files[] = $raw_path;
|
||||
continue;
|
||||
}
|
||||
if (@pathinfo($raw_path)['extension'] == 'sql') {
|
||||
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
|
||||
\Log::debug("Found a sql file!");
|
||||
$sqlfiles[] = $raw_path;
|
||||
$sqlfile_indices[] = $i;
|
||||
|
|
|
@ -63,6 +63,7 @@ class AccessoriesController extends Controller
|
|||
public function store(ImageUploadRequest $request)
|
||||
{
|
||||
$this->authorize(Accessory::class);
|
||||
|
||||
// create a new model instance
|
||||
$accessory = new Accessory();
|
||||
|
||||
|
@ -82,7 +83,6 @@ class AccessoriesController extends Controller
|
|||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory->notes = request('notes');
|
||||
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
// Was the accessory created?
|
||||
|
@ -127,45 +127,47 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null)
|
||||
{
|
||||
if (is_null($accessory = Accessory::find($accessoryId))) {
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
"qty" => "required|numeric|min:$accessory->users_count"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->name = request('name');
|
||||
$accessory->location_id = request('location_id');
|
||||
$accessory->min_amt = request('min_amt');
|
||||
$accessory->category_id = request('category_id');
|
||||
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
|
||||
$accessory->manufacturer_id = request('manufacturer_id');
|
||||
$accessory->order_number = request('order_number');
|
||||
$accessory->model_number = request('model_number');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory->notes = request('notes');
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
// Was the accessory updated?
|
||||
if ($accessory->save()) {
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
} else {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$min = $accessory->numCheckedOut();
|
||||
$validator = Validator::make($request->all(), [
|
||||
"qty" => "required|numeric|min:$min"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
// Update the accessory data
|
||||
$accessory->name = request('name');
|
||||
$accessory->location_id = request('location_id');
|
||||
$accessory->min_amt = request('min_amt');
|
||||
$accessory->category_id = request('category_id');
|
||||
$accessory->company_id = Company::getIdForCurrentUser(request('company_id'));
|
||||
$accessory->manufacturer_id = request('manufacturer_id');
|
||||
$accessory->order_number = request('order_number');
|
||||
$accessory->model_number = request('model_number');
|
||||
$accessory->purchase_date = request('purchase_date');
|
||||
$accessory->purchase_cost = Helper::ParseCurrency(request('purchase_cost'));
|
||||
$accessory->qty = request('qty');
|
||||
$accessory->supplier_id = request('supplier_id');
|
||||
$accessory->notes = request('notes');
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
// Was the accessory updated?
|
||||
if ($accessory->save()) {
|
||||
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
|
||||
}
|
||||
|
||||
|
@ -217,7 +219,7 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function show($accessoryID = null)
|
||||
{
|
||||
$accessory = Accessory::find($accessoryID);
|
||||
$accessory = Accessory::withCount('users as users_count')->find($accessoryID);
|
||||
$this->authorize('view', $accessory);
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
|
|
|
@ -222,8 +222,8 @@ class AcceptanceController extends Controller
|
|||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'eula' => $item->getEula(),
|
||||
'check_out_date' => Carbon::parse($acceptance->created_at)->format($branding_settings->date_display_format),
|
||||
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format),
|
||||
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
|
||||
'accepted_date' => Carbon::parse($acceptance->accepted_at)->format('Y-m-d'),
|
||||
'assigned_to' => $assigned_to,
|
||||
'company_name' => $branding_settings->site_name,
|
||||
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
|
||||
|
@ -273,7 +273,7 @@ class AcceptanceController extends Controller
|
|||
'item_tag' => $item->asset_tag,
|
||||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'declined_date' => Carbon::parse($acceptance->accepted_at)->format($branding_settings->date_display_format),
|
||||
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
|
||||
'assigned_to' => $assigned_to,
|
||||
'company_name' => $branding_settings->site_name,
|
||||
'date_settings' => $branding_settings->date_display_format,
|
||||
|
|
|
@ -41,10 +41,13 @@ class AccessoriesController extends Controller
|
|||
'min_amt',
|
||||
'company_id',
|
||||
'notes',
|
||||
'users_count',
|
||||
'qty',
|
||||
];
|
||||
|
||||
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier');
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
|
||||
->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
|
|
|
@ -33,7 +33,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index', CustomFieldset::class);
|
||||
$this->authorize('index', CustomField::class);
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
|
||||
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
|
||||
|
@ -49,7 +49,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
$this->authorize('view', CustomField::class);
|
||||
if ($fieldset = CustomFieldset::find($id)) {
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldset($fieldset);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$this->authorize('update', CustomField::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
|
@ -89,7 +89,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
$this->authorize('create', CustomField::class);
|
||||
$fieldset = new CustomFieldset;
|
||||
$fieldset->fill($request->all());
|
||||
|
||||
|
@ -109,7 +109,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->authorize('delete', CustomFieldset::class);
|
||||
$this->authorize('delete', CustomField::class);
|
||||
$fieldset = CustomFieldset::findOrFail($id);
|
||||
|
||||
$modelsCount = $fieldset->models->count();
|
||||
|
@ -136,7 +136,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function fields($id)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
$this->authorize('view', CustomField::class);
|
||||
$set = CustomFieldset::findOrFail($id);
|
||||
$fields = $set->fields;
|
||||
|
||||
|
@ -153,7 +153,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function fieldsWithDefaultValues($fieldsetId, $modelId)
|
||||
{
|
||||
$this->authorize('view', CustomFieldset::class);
|
||||
$this->authorize('view', CustomField::class);
|
||||
|
||||
$set = CustomFieldset::findOrFail($fieldsetId);
|
||||
|
||||
|
|
|
@ -542,9 +542,10 @@ class UsersController extends Controller
|
|||
if (empty($user->email)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.inventorynotification.error')));
|
||||
}
|
||||
|
||||
$user->notify((new CurrentInventory($user)));
|
||||
|
||||
return response()->Helper::formatStandardApiResponse('success', null, trans('admin/users/message.inventorynotification.success'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.inventorynotification.success')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,9 +75,9 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
return view('custom_fields.fieldsets.edit');
|
||||
return view('custom_fields.fieldsets.edit')->with('item', new CustomFieldset());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +91,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorize('create', CustomFieldset::class);
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
$cfset = new CustomFieldset([
|
||||
'name' => e($request->get('name')),
|
||||
|
@ -110,31 +110,52 @@ class CustomFieldsetsController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* What the actual fuck, Brady?
|
||||
* Presents edit form for fieldset
|
||||
*
|
||||
* @todo Uhh, build this?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
* @since [v6.0.14]
|
||||
* @return Redirect
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
if ($fieldset = CustomFieldset::find($id)) {
|
||||
return view('custom_fields.fieldsets.edit')->with('item', $fieldset);
|
||||
}
|
||||
|
||||
return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* GET IN THE SEA BRADY.
|
||||
* Saves updated fieldset data
|
||||
*
|
||||
* @todo Uhh, build this too?
|
||||
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param int $id
|
||||
* @since [v1.8]
|
||||
* @return Fuckall
|
||||
* @since [v6.0.14]
|
||||
* @return Redirect
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update($id)
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
$this->authorize('create', CustomField::class);
|
||||
|
||||
if ($fieldset = CustomFieldset::find($id)) {
|
||||
|
||||
$fieldset->name = $request->input('name');
|
||||
|
||||
if ($fieldset->save()) {
|
||||
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/general.fieldset_updated'));
|
||||
}
|
||||
|
||||
return redirect()->back()->withInput()->withErrors($fieldset->getErrors());
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('fields.index')->with('error', trans('admin/custom_fields/general.fieldset_does_not_exist', ['id' => $id]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,7 +169,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$fieldset = CustomFieldset::find($id);
|
||||
$fieldset = CustomField::find($id);
|
||||
|
||||
$this->authorize('delete', $fieldset);
|
||||
|
||||
|
@ -175,7 +196,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function associate(Request $request, $id)
|
||||
{
|
||||
$set = CustomFieldset::find($id);
|
||||
$set = CustomField::find($id);
|
||||
|
||||
$this->authorize('update', $set);
|
||||
|
||||
|
@ -202,7 +223,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function makeFieldRequired($fieldset_id, $field_id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$this->authorize('update', CustomField::class);
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
$fields[$field->id] = ['required' => 1];
|
||||
|
@ -220,7 +241,7 @@ class CustomFieldsetsController extends Controller
|
|||
*/
|
||||
public function makeFieldOptional($fieldset_id, $field_id)
|
||||
{
|
||||
$this->authorize('update', CustomFieldset::class);
|
||||
$this->authorize('update', CustomField::class);
|
||||
$field = CustomField::findOrFail($field_id);
|
||||
$fieldset = CustomFieldset::findOrFail($fieldset_id);
|
||||
$fields[$field->id] = ['required' => 0];
|
||||
|
|
|
@ -38,7 +38,8 @@ class AccessoriesTransformer
|
|||
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
|
||||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => $accessory->numRemaining(),
|
||||
'remaining_qty' => (int) $accessory->numRemaining(),
|
||||
'users_count' => $accessory->users_count,
|
||||
|
||||
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
||||
|
|
|
@ -327,20 +327,6 @@ class Accessory extends SnipeModel
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how many items within an accessory are checked out
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v5.0]
|
||||
* @return int
|
||||
*/
|
||||
public function numCheckedOut()
|
||||
{
|
||||
$checkedout = 0;
|
||||
$checkedout = $this->users->count();
|
||||
|
||||
return $checkedout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how many items of an accessory remain
|
||||
|
@ -351,11 +337,11 @@ class Accessory extends SnipeModel
|
|||
*/
|
||||
public function numRemaining()
|
||||
{
|
||||
$checkedout = $this->users->count();
|
||||
$checkedout = $this->users_count;
|
||||
$total = $this->qty;
|
||||
$remaining = $total - $checkedout;
|
||||
|
||||
return $remaining;
|
||||
return (int) $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,94 +12,9 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig
|
|||
{
|
||||
public function getUserConfig()
|
||||
{
|
||||
$config = parent::getUserConfig();
|
||||
|
||||
// Much of this is copied verbatim from the library, then adjusted for our needs
|
||||
$config['class'] = SCIMUser::class;
|
||||
|
||||
unset($config['mapping']['example:name:space']);
|
||||
|
||||
$config['map_unmapped'] = false; // anything we don't explicitly map will _not_ show up.
|
||||
|
||||
$core_namespace = 'urn:ietf:params:scim:schemas:core:2.0:User';
|
||||
$core = $core_namespace.':';
|
||||
$mappings =& $config['mapping'][$core_namespace]; //grab this entire key, we don't want to be repeating ourselves
|
||||
|
||||
//username - *REQUIRED*
|
||||
$config['validations'][$core.'userName'] = 'required';
|
||||
$mappings['userName'] = AttributeMapping::eloquent('username');
|
||||
|
||||
//human name - *FIRST NAME REQUIRED*
|
||||
$config['validations'][$core.'name.givenName'] = 'required';
|
||||
$config['validations'][$core.'name.familyName'] = 'string'; //not required
|
||||
|
||||
$mappings['name']['familyName'] = AttributeMapping::eloquent("last_name");
|
||||
$mappings['name']['givenName'] = AttributeMapping::eloquent("first_name");
|
||||
$mappings['name']['formatted'] = (new AttributeMapping())->ignoreWrite()->setRead(
|
||||
function (&$object) {
|
||||
return $object->getFullNameAttribute();
|
||||
}
|
||||
);
|
||||
|
||||
// externalId support
|
||||
$config['validations'][$core.'externalId'] = 'string|nullable'; // not required, but supported mostly just for Okta
|
||||
// note that the mapping is *not* namespaced like the other $mappings
|
||||
$config['mapping']['externalId'] = AttributeMapping::eloquent('scim_externalid');
|
||||
|
||||
$config['validations'][$core.'emails'] = 'nullable|array'; // emails are not required in Snipe-IT...
|
||||
$config['validations'][$core.'emails.*.value'] = 'email'; // ...(had to remove the recommended 'required' here)
|
||||
|
||||
$mappings['emails'] = [[
|
||||
"value" => AttributeMapping::eloquent("email"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]];
|
||||
|
||||
//active
|
||||
$config['validations'][$core.'active'] = 'boolean';
|
||||
|
||||
$mappings['active'] = AttributeMapping::eloquent('activated');
|
||||
|
||||
//phone
|
||||
$config['validations'][$core.'phoneNumbers'] = 'nullable|array';
|
||||
$config['validations'][$core.'phoneNumbers.*.value'] = 'string'; // another one where want to say 'we don't _need_ a phone number, but if you have one it better have a value.
|
||||
|
||||
$mappings['phoneNumbers'] = [[
|
||||
"value" => AttributeMapping::eloquent("phone"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]];
|
||||
|
||||
//address
|
||||
$config['validations'][$core.'addresses'] = 'nullable|array';
|
||||
$config['validations'][$core.'addresses.*.streetAddress'] = 'string';
|
||||
$config['validations'][$core.'addresses.*.locality'] = 'string';
|
||||
$config['validations'][$core.'addresses.*.region'] = 'nullable|string';
|
||||
$config['validations'][$core.'addresses.*.postalCode'] = 'nullable|string';
|
||||
$config['validations'][$core.'addresses.*.country'] = 'string';
|
||||
|
||||
$mappings['addresses'] = [[
|
||||
'type' => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
'formatted' => AttributeMapping::constant("n/a")->ignoreWrite(), // TODO - is this right? This doesn't look right.
|
||||
'streetAddress' => AttributeMapping::eloquent("address"),
|
||||
'locality' => AttributeMapping::eloquent("city"),
|
||||
'region' => AttributeMapping::eloquent("state"),
|
||||
'postalCode' => AttributeMapping::eloquent("zip"),
|
||||
'country' => AttributeMapping::eloquent("country"),
|
||||
'primary' => AttributeMapping::constant(true)->ignoreWrite() //this isn't in the example?
|
||||
]];
|
||||
|
||||
//title
|
||||
$config['validations'][$core.'title'] = 'string';
|
||||
$mappings['title'] = AttributeMapping::eloquent('jobtitle');
|
||||
|
||||
//Preferred Language
|
||||
$config['validations'][$core.'preferredLanguage'] = 'string';
|
||||
$mappings['preferredLanguage'] = AttributeMapping::eloquent('locale');
|
||||
|
||||
/*
|
||||
/*
|
||||
more snipe-it attributes I'd like to check out (to map to 'enterprise' maybe?):
|
||||
- website
|
||||
- notes?
|
||||
|
@ -108,66 +23,213 @@ class SnipeSCIMConfig extends \ArieTimmerman\Laravel\SCIMServer\SCIMConfig
|
|||
- company_id to "organization?"
|
||||
*/
|
||||
|
||||
$enterprise_namespace = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';
|
||||
$ent = $enterprise_namespace.':';
|
||||
|
||||
// we remove the 'example' namespace and add the Enterprise one
|
||||
$config['mapping']['schemas'] = AttributeMapping::constant( [$core_namespace, $enterprise_namespace] )->ignoreWrite();
|
||||
$user_prefix = 'urn:ietf:params:scim:schemas:core:2.0:User:';
|
||||
$enterprise_prefix = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:';
|
||||
|
||||
$config['validations'][$ent.'employeeNumber'] = 'string';
|
||||
$config['validations'][$ent.'department'] = 'string';
|
||||
$config['validations'][$ent.'manager'] = 'nullable';
|
||||
$config['validations'][$ent.'manager.value'] = 'string';
|
||||
return [
|
||||
|
||||
$config['mapping'][$enterprise_namespace] = [
|
||||
'employeeNumber' => AttributeMapping::eloquent('employee_num'),
|
||||
'department' =>(new AttributeMapping())->setAdd( // FIXME parent?
|
||||
function ($value, &$object) {
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
}
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
// Set to 'null' to make use of auth.providers.users.model (App\User::class)
|
||||
'class' => SCIMUser::class,
|
||||
|
||||
'validations' => [
|
||||
$user_prefix . 'userName' => 'required',
|
||||
$user_prefix . 'name.givenName' => 'required',
|
||||
$user_prefix . 'name.familyName' => 'nullable|string',
|
||||
$user_prefix . 'externalId' => 'nullable|string',
|
||||
$user_prefix . 'emails' => 'nullable|array',
|
||||
$user_prefix . 'emails.*.value' => 'nullable|email',
|
||||
$user_prefix . 'active' => 'boolean',
|
||||
$user_prefix . 'phoneNumbers' => 'nullable|array',
|
||||
$user_prefix . 'phoneNumbers.*.value' => 'nullable|string',
|
||||
$user_prefix . 'addresses' => 'nullable|array',
|
||||
$user_prefix . 'addresses.*.streetAddress' => 'nullable|string',
|
||||
$user_prefix . 'addresses.*.locality' => 'nullable|string',
|
||||
$user_prefix . 'addresses.*.region' => 'nullable|string',
|
||||
$user_prefix . 'addresses.*.postalCode' => 'nullable|string',
|
||||
$user_prefix . 'addresses.*.country' => 'nullable|string',
|
||||
$user_prefix . 'title' => 'nullable|string',
|
||||
$user_prefix . 'preferredLanguage' => 'nullable|string',
|
||||
|
||||
// Enterprise validations:
|
||||
$enterprise_prefix . 'employeeNumber' => 'nullable|string',
|
||||
$enterprise_prefix . 'department' => 'nullable|string',
|
||||
$enterprise_prefix . 'manager' => 'nullable',
|
||||
$enterprise_prefix . 'manager.value' => 'nullable|string'
|
||||
],
|
||||
|
||||
'singular' => 'User',
|
||||
'schema' => [Schema::SCHEMA_USER],
|
||||
|
||||
//eager loading
|
||||
'withRelations' => [],
|
||||
'map_unmapped' => false,
|
||||
// 'unmapped_namespace' => 'urn:ietf:params:scim:schemas:laravel:unmapped',
|
||||
'description' => 'User Account',
|
||||
|
||||
// Map a SCIM attribute to an attribute of the object.
|
||||
'mapping' => [
|
||||
|
||||
'id' => AttributeMapping::eloquent("id")->disableWrite(),
|
||||
|
||||
'externalId' => AttributeMapping::eloquent('scim_externalid'), // FIXME - I have a PR that changes a lot of this.
|
||||
|
||||
'meta' => [
|
||||
'created' => AttributeMapping::eloquent("created_at")->disableWrite(),
|
||||
'lastModified' => AttributeMapping::eloquent("updated_at")->disableWrite(),
|
||||
|
||||
'location' => (new AttributeMapping())->setRead(
|
||||
function ($object) {
|
||||
return route(
|
||||
'scim.resource',
|
||||
[
|
||||
'resourceType' => 'Users',
|
||||
'resourceObject' => $object->id
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
)->setRead(
|
||||
function (&$object) {
|
||||
return $object->department ? $object->department->name : null;
|
||||
}
|
||||
),
|
||||
'manager' => [
|
||||
// FIXME - manager writes are disabled. This kinda works but it leaks errors all over the place. Not cool.
|
||||
// '$ref' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// 'displayName' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// NOTE: you could probably do a 'plain' Eloquent mapping here, but we don't for future-proofing
|
||||
'value' => (new AttributeMapping())->setAdd(
|
||||
function ($value, &$object) {
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
)->disableWrite(),
|
||||
|
||||
'resourceType' => AttributeMapping::constant("User")
|
||||
],
|
||||
|
||||
'schemas' => AttributeMapping::constant(
|
||||
[
|
||||
'urn:ietf:params:scim:schemas:core:2.0:User',
|
||||
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'
|
||||
]
|
||||
)->ignoreWrite(),
|
||||
|
||||
'urn:ietf:params:scim:schemas:core:2.0:User' => [
|
||||
|
||||
'userName' => AttributeMapping::eloquent("username"),
|
||||
|
||||
'name' => [
|
||||
'formatted' => (new AttributeMapping())->ignoreWrite()->setRead(
|
||||
function (&$object) {
|
||||
return $object->getFullNameAttribute();
|
||||
}
|
||||
),
|
||||
'familyName' => AttributeMapping::eloquent("last_name"),
|
||||
'givenName' => AttributeMapping::eloquent("first_name"),
|
||||
'middleName' => null,
|
||||
'honorificPrefix' => null,
|
||||
'honorificSuffix' => null
|
||||
],
|
||||
|
||||
'displayName' => null,
|
||||
'nickName' => null,
|
||||
'profileUrl' => null,
|
||||
'title' => AttributeMapping::eloquent('jobtitle'),
|
||||
'userType' => null,
|
||||
'preferredLanguage' => AttributeMapping::eloquent('locale'), // Section 5.3.5 of [RFC7231]
|
||||
'locale' => null, // see RFC5646
|
||||
'timezone' => null, // see RFC6557
|
||||
'active' => AttributeMapping::eloquent('activated'),
|
||||
|
||||
'password' => AttributeMapping::eloquent('password')->disableRead(),
|
||||
|
||||
// Multi-Valued Attributes
|
||||
'emails' => [[
|
||||
"value" => AttributeMapping::eloquent("email"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]],
|
||||
|
||||
'phoneNumbers' => [[
|
||||
"value" => AttributeMapping::eloquent("phone"),
|
||||
"display" => null,
|
||||
"type" => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
"primary" => AttributeMapping::constant(true)->ignoreWrite()
|
||||
]],
|
||||
|
||||
'ims' => [[
|
||||
"value" => null,
|
||||
"display" => null,
|
||||
"type" => null,
|
||||
"primary" => null
|
||||
]], // Instant messaging addresses for the User
|
||||
|
||||
'photos' => [[
|
||||
"value" => null,
|
||||
"display" => null,
|
||||
"type" => null,
|
||||
"primary" => null
|
||||
]],
|
||||
|
||||
'addresses' => [[
|
||||
'type' => AttributeMapping::constant("work")->ignoreWrite(),
|
||||
'formatted' => AttributeMapping::constant("n/a")->ignoreWrite(), // TODO - is this right? This doesn't look right.
|
||||
'streetAddress' => AttributeMapping::eloquent("address"),
|
||||
'locality' => AttributeMapping::eloquent("city"),
|
||||
'region' => AttributeMapping::eloquent("state"),
|
||||
'postalCode' => AttributeMapping::eloquent("zip"),
|
||||
'country' => AttributeMapping::eloquent("country"),
|
||||
'primary' => AttributeMapping::constant(true)->ignoreWrite() //this isn't in the example?
|
||||
]],
|
||||
|
||||
'groups' => [[
|
||||
'value' => null,
|
||||
'$ref' => null,
|
||||
'display' => null,
|
||||
'type' => null,
|
||||
'type' => null
|
||||
]],
|
||||
|
||||
'entitlements' => null,
|
||||
'roles' => null,
|
||||
'x509Certificates' => null
|
||||
],
|
||||
|
||||
'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' => [
|
||||
'employeeNumber' => AttributeMapping::eloquent('employee_num'),
|
||||
'department' => (new AttributeMapping())->setAdd( // FIXME parent?
|
||||
function ($value, &$object) {
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
$department = Department::where("name", $value)->first();
|
||||
if ($department) {
|
||||
$object->department_id = $department->id;
|
||||
}
|
||||
}
|
||||
)->setRead(
|
||||
function (&$object) {
|
||||
return $object->manager_id;
|
||||
}
|
||||
return $object->department ? $object->department->name : null;
|
||||
}
|
||||
),
|
||||
'manager' => [
|
||||
// FIXME - manager writes are disabled. This kinda works but it leaks errors all over the place. Not cool.
|
||||
// '$ref' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// 'displayName' => (new AttributeMapping())->ignoreWrite()->ignoreRead(),
|
||||
// NOTE: you could probably do a 'plain' Eloquent mapping here, but we don't for future-proofing
|
||||
'value' => (new AttributeMapping())->setAdd(
|
||||
function ($value, &$object) {
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
}
|
||||
}
|
||||
)->setReplace(
|
||||
function ($value, &$object) {
|
||||
$manager = User::find($value);
|
||||
if ($manager) {
|
||||
$object->manager_id = $manager->id;
|
||||
}
|
||||
}
|
||||
)->setRead(
|
||||
function (&$object) {
|
||||
return $object->manager_id;
|
||||
}
|
||||
),
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
0
app/Models/User.php
Executable file → Normal file
0
app/Models/User.php
Executable file → Normal file
|
@ -80,19 +80,25 @@ class AccessoryPresenter extends Presenter
|
|||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('admin/accessories/general.total'),
|
||||
], [
|
||||
'field' => 'min_qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'title' => trans('admin/accessories/general.total'),
|
||||
], [
|
||||
'field' => 'remaining_qty',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
],[
|
||||
'field' => 'users_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
'title' => trans('general.checked_out'),
|
||||
], [
|
||||
'field' => 'min_qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
], [
|
||||
'field' => 'purchase_date',
|
||||
'searchable' => true,
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.16",
|
||||
"laravel/dusk": "^6.19",
|
||||
"laravel/dusk": "^6.25",
|
||||
"mockery/mockery": "^1.4",
|
||||
"phpunit/php-token-stream": "^3.1",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
|
|
22
composer.lock
generated
22
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0c1f3848f8c9ede2b5f1513e4feaa7d7",
|
||||
"content-hash": "4fed0ab76a34ef85a44568e470abab48",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
|
@ -78,12 +78,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/grokability/laravel-scim-server.git",
|
||||
"reference": "2c7ecc450eee59234e059ec2e7724b2d8f3a8369"
|
||||
"reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/2c7ecc450eee59234e059ec2e7724b2d8f3a8369",
|
||||
"reference": "2c7ecc450eee59234e059ec2e7724b2d8f3a8369",
|
||||
"url": "https://api.github.com/repos/grokability/laravel-scim-server/zipball/9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419",
|
||||
"reference": "9e8dd2d3958d3c3c05d0a99fe6475361ad9e9419",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -133,7 +133,7 @@
|
|||
"support": {
|
||||
"source": "https://github.com/grokability/laravel-scim-server/tree/master"
|
||||
},
|
||||
"time": "2022-11-22T20:26:54+00:00"
|
||||
"time": "2023-01-12T00:32:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
|
@ -11847,16 +11847,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/dusk",
|
||||
"version": "v6.25.0",
|
||||
"version": "v6.25.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/dusk.git",
|
||||
"reference": "b4632b7493a187d31afc5c9ddec437c81b16421a"
|
||||
"reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/dusk/zipball/b4632b7493a187d31afc5c9ddec437c81b16421a",
|
||||
"reference": "b4632b7493a187d31afc5c9ddec437c81b16421a",
|
||||
"url": "https://api.github.com/repos/laravel/dusk/zipball/25a595ac3dc82089a91af10dd23b0d58fd3f6d0b",
|
||||
"reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -11914,9 +11914,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/dusk/issues",
|
||||
"source": "https://github.com/laravel/dusk/tree/v6.25.0"
|
||||
"source": "https://github.com/laravel/dusk/tree/v6.25.2"
|
||||
},
|
||||
"time": "2022-07-11T11:38:43+00:00"
|
||||
"time": "2022-09-29T09:37:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
|
|
|
@ -102,6 +102,7 @@ return [
|
|||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
return [
|
||||
"trace" => env("SCIM_TRACE",false),
|
||||
// below, if we ever get 'sure' that we can change this default to 'true' we should
|
||||
"omit_main_schema_in_return" => env('SCIM_STANDARDS_COMPLIANCE', false),
|
||||
"omit_main_schema_in_return" => env('SCIM_STANDARDS_COMPLIANCE', true),
|
||||
"publish_routes" => false,
|
||||
];
|
||||
|
|
|
@ -44,6 +44,8 @@ class MigrationCartalystSentryInstallGroups extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('permission_groups');
|
||||
// See 2014_11_04_231416_update_group_field_for_reporting.php and 2019_06_12_184327_rename_groups_table.php
|
||||
Schema::dropIfExists('permission_groups');
|
||||
Schema::dropIfExists('groups');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,6 @@ class AddPhysicalToAssets extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
$table->dropColumn('physical');
|
||||
// $table->dropColumn('physical');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class ReCreateLicensesTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::drop('licenses');
|
||||
// This was most likely handled in 2013_11_17_054359_drop_licenses_table.php
|
||||
Schema::dropIfExists('licenses');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,6 @@ class CreateLicenseSeatsTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::dropIfExists('license_seats');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ class AlterWarrantyColumnOnAssets extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
Schema::table('assets', function ($table) {
|
||||
$table->renameColumn('warranty_months', 'warrantee_months');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class AddEolOnModelsTable extends Migration
|
|||
public function down()
|
||||
{
|
||||
Schema::table('models', function ($table) {
|
||||
$table->dropColumn('old');
|
||||
$table->dropColumn('eol');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,10 +105,10 @@
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('asset_logs', function (Blueprint $table) {
|
||||
$table->dropIndex('thread_id');
|
||||
$table->dropColumn('thread_id');
|
||||
});
|
||||
// Schema::table('asset_logs', function (Blueprint $table) {
|
||||
// $table->dropIndex('thread_id');
|
||||
// $table->dropColumn('thread_id');
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,13 +48,19 @@ class MigrateMacAddress extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
$f = \App\Models\CustomFieldset::where(['name' => 'Asset with MAC Address'])->first();
|
||||
$f->fields()->delete();
|
||||
$f->delete();
|
||||
|
||||
if ($f) {
|
||||
$f->fields()->delete();
|
||||
$f->delete();
|
||||
}
|
||||
|
||||
Schema::table('models', function (Blueprint $table) {
|
||||
$table->renameColumn('deprecated_mac_address', 'show_mac_address');
|
||||
});
|
||||
DB::statement('ALTER TABLE assets CHANGE _snipeit_mac_address mac_address varchar(255)');
|
||||
|
||||
if (Schema::hasColumn('assets', '_snipeit_mac_address')) {
|
||||
DB::statement('ALTER TABLE assets CHANGE _snipeit_mac_address mac_address varchar(255)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class AddShowInNavToStatusLabels extends Migration
|
|||
public function down()
|
||||
{
|
||||
Schema::table('status_labels', function (Blueprint $table) {
|
||||
$table->dropColumn('show_in_nav', 'field_encrypted');
|
||||
$table->dropColumn('show_in_nav');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use App\Models\CustomField;
|
|||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Fixes issue #2551 where columns got donked if the field name in non-ascii
|
||||
|
@ -71,6 +72,25 @@ class FixUtf8CustomFieldColumnNames extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
// In the up method above, updateLegacyColumnName is called and custom fields in the assets table are prefixed
|
||||
// with "_snipe_it_", suffixed with "_{id of the CustomField}", and stored in custom_fields.db_column.
|
||||
// The following reverses those changes.
|
||||
foreach (CustomField::all() as $field) {
|
||||
$currentColumnName = $field->db_column;
|
||||
|
||||
// "_snipeit_imei_1" becomes "_snipeit_imei"
|
||||
$legacyColumnName = (string) Str::of($currentColumnName)->replaceMatches('/_(\d)+$/', '');
|
||||
|
||||
if (Schema::hasColumn(CustomField::$table_name, $currentColumnName)) {
|
||||
Schema::table(CustomField::$table_name, function (Blueprint $table) use ($currentColumnName, $legacyColumnName) {
|
||||
$table->renameColumn(
|
||||
$currentColumnName,
|
||||
$legacyColumnName
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Schema::table('custom_fields', function ($table) {
|
||||
$table->dropColumn('db_column');
|
||||
$table->dropColumn('help_text');
|
||||
|
|
|
@ -28,7 +28,7 @@ class AddFieldsToManufacturer extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('settings', function ($table) {
|
||||
Schema::table('manufacturers', function ($table) {
|
||||
$table->dropColumn('url');
|
||||
$table->dropColumn('support_url');
|
||||
$table->dropColumn('support_phone');
|
||||
|
|
BIN
public/css/dist/skins/skin-contrast.css
vendored
BIN
public/css/dist/skins/skin-contrast.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
Binary file not shown.
BIN
public/img/snipe-logo-bug.png
Normal file
BIN
public/img/snipe-logo-bug.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -16,7 +16,7 @@
|
|||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=c0d21166315b7c2cdd4819fa4a5e4d1e",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=8e538625ebd4b8096e150d1aa483547b",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=ef030b613d45620b907cf0184a14e868",
|
||||
"/css/blue.png": "/css/blue.png?id=e83a6c29e04fe851f2122815b2e4b150",
|
||||
|
@ -49,5 +49,5 @@
|
|||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=713b1205aa2d7c9db282f8cd5754c0e4",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=f343f659ca1d45534d2c2c3cc30fb619",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=8e538625ebd4b8096e150d1aa483547b"
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da"
|
||||
}
|
||||
|
|
|
@ -84,7 +84,13 @@
|
|||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
a.btn.btn-link.text-left{
|
||||
color:@navy;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
a.btn.btn-link.text-left:hover{
|
||||
color:@navy;
|
||||
}
|
||||
|
||||
a {
|
||||
color: @navy;
|
||||
|
@ -102,10 +108,6 @@ a.btn {
|
|||
&:hover {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
|
||||
}
|
||||
&:visited {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ return [
|
|||
'used_by_models' => 'Used By Models',
|
||||
'order' => 'Order',
|
||||
'create_fieldset' => 'New Fieldset',
|
||||
'update_fieldset' => 'Update Fieldset',
|
||||
'fieldset_does_not_exist' => 'Fieldset :id does not exist',
|
||||
'fieldset_updated' => 'Fieldset updated',
|
||||
'create_fieldset_title' => 'Create a new fieldset',
|
||||
'create_field' => 'New Custom Field',
|
||||
'create_field_title' => 'Create a new custom field',
|
||||
|
|
|
@ -46,4 +46,4 @@ return [
|
|||
'email_credentials' => 'Email credentials',
|
||||
'email_credentials_text' => 'Email my credentials to the email address above',
|
||||
'next_save_user' => 'Next: Save User',
|
||||
];
|
||||
];
|
|
@ -390,7 +390,8 @@ return [
|
|||
'start_date' => 'Start Date',
|
||||
'end_date' => 'End Date',
|
||||
'alt_uploaded_image_thumbnail' => 'Uploaded thumbnail',
|
||||
'placeholder_kit' => 'Select a kit'
|
||||
'placeholder_kit' => 'Select a kit',
|
||||
'file_not_found' => 'File not found',
|
||||
|
||||
|
||||
|
||||
|
|
0
resources/views/accessories/edit.blade.php
Executable file → Normal file
0
resources/views/accessories/edit.blade.php
Executable file → Normal file
|
@ -290,7 +290,7 @@
|
|||
@if ($accessory->company)
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="padding-bottom: 15px;">
|
||||
{{ trans('general.company')}}
|
||||
<strong> {{ trans('general.company')}}</strong>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<a href="{{ route('companies.show', $accessory->company->id) }}">{{ $accessory->company->name }} </a>
|
||||
|
@ -302,7 +302,7 @@
|
|||
@if ($accessory->category)
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="padding-bottom: 15px;">
|
||||
{{ trans('general.category')}}
|
||||
<strong>{{ trans('general.category')}}</strong>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<a href="{{ route('categories.show', $accessory->category->id) }}">{{ $accessory->category->name }} </a>
|
||||
|
@ -327,13 +327,22 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="padding-bottom: 15px;">
|
||||
Number remaining
|
||||
<strong>{{ trans('admin/accessories/general.remaining') }}</strong>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ $accessory->numRemaining() }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4" style="padding-bottom: 15px;">
|
||||
<strong>{{ trans('general.checked_out') }}</strong>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ $accessory->users_count }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@can('checkout', \App\Models\Accessory::class)
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
@if ($snipeSettings->slack_endpoint!='')
|
||||
<i class="fab fa-slack"></i>
|
||||
A slack message will be sent
|
||||
{{ trans('general.slack_msg_note') }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,49 +1,17 @@
|
|||
@extends('layouts.default')
|
||||
@extends('layouts/edit-form', [
|
||||
'createText' => trans('admin/custom_fields/general.create_fieldset') ,
|
||||
'updateText' => trans('admin/custom_fields/general.update_fieldset'),
|
||||
'helpText' => trans('admin/custom_fields/general.about_fieldsets_text'),
|
||||
'helpPosition' => 'right',
|
||||
'formAction' => (isset($item->id)) ? route('fieldsets.update', ['fieldset' => $item->id]) : route('fieldsets.store'),
|
||||
])
|
||||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
{{ trans('admin/custom_fields/general.create_fieldset') }}
|
||||
@parent
|
||||
@stop
|
||||
|
||||
@section('header_right')
|
||||
<a href="{{ URL::previous() }}" class="btn btn-primary pull-right">
|
||||
{{ trans('general.back') }}</a>
|
||||
@stop
|
||||
|
||||
|
||||
{{-- Page content --}}
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
|
||||
{{ Form::open(['route' => 'fieldsets.store', 'class'=>'form-horizontal']) }}
|
||||
<!-- Horizontal Form -->
|
||||
<div class="box box-default">
|
||||
<div class="box-body">
|
||||
|
||||
<!-- Name -->
|
||||
<div class="form-group {{ $errors->has('name') ? ' has-error' : '' }}">
|
||||
<label for="name" class="col-md-4 control-label">
|
||||
{{ trans('admin/custom_fields/general.fieldset_name') }}
|
||||
</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="text" name="name" id="name" value="{{ old('name') }}" required>
|
||||
{!! $errors->first('name', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /.box-body-->
|
||||
<div class="box-footer text-right">
|
||||
<button type="submit" class="btn btn-success"><i class="fas fa-check icon-white" aria-hidden="true"></i> {{ trans('general.save') }}</button>
|
||||
</div>
|
||||
|
||||
</div> <!-- /.box.box-default-->
|
||||
{{ Form::close() }}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h2>{{ trans('admin/custom_fields/general.about_fieldsets_title') }}</h4>
|
||||
<p>{{ trans('admin/custom_fields/general.about_fieldsets_text') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@parent
|
||||
@stop
|
||||
|
||||
@section('inputFields')
|
||||
@include ('partials.forms.edit.name', ['translated_name' => trans('general.name')])
|
||||
@stop
|
||||
|
||||
|
||||
|
|
|
@ -69,8 +69,18 @@
|
|||
@endforeach
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<nobr>
|
||||
|
||||
@can('update', $fieldset)
|
||||
<a href="{{ route('fieldsets.edit', $fieldset->id) }}" class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-pencil-alt" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('button.edit') }}</span>
|
||||
</a>
|
||||
@endcan
|
||||
|
||||
@can('delete', $fieldset)
|
||||
{{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete']) }}
|
||||
{{ Form::open(['route' => array('fieldsets.destroy', $fieldset->id), 'method' => 'delete','style' => 'display:inline-block']) }}
|
||||
@if($fieldset->models->count() > 0)
|
||||
<button type="submit" class="btn btn-danger btn-sm disabled" disabled><i class="fas fa-trash"></i></button>
|
||||
@else
|
||||
|
@ -78,6 +88,7 @@
|
|||
@endif
|
||||
{{ Form::close() }}
|
||||
@endcan
|
||||
</nobr>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" href="{{ ($snipeSettings) && ($snipeSettings->favicon!='') ? Storage::disk('public')->url(e($snipeSettings->logo)) : config('app.url').'/img/logo.png' }}">
|
||||
<link rel="apple-touch-startup-image" href="{{ ($snipeSettings) && ($snipeSettings->favicon!='') ? Storage::disk('public')->url(e($snipeSettings->logo)) : config('app.url').'/img/logo.png' }}">
|
||||
<link rel="apple-touch-icon" href="{{ ($snipeSettings) && ($snipeSettings->favicon!='') ? Storage::disk('public')->url(e($snipeSettings->logo)) : config('app.url').'/img/snipe-logo-bug.png' }}">
|
||||
<link rel="apple-touch-startup-image" href="{{ ($snipeSettings) && ($snipeSettings->favicon!='') ? Storage::disk('public')->url(e($snipeSettings->logo)) : config('app.url').'/img/snipe-logo-bug.png' }}">
|
||||
<link rel="shortcut icon" type="image/ico" href="{{ ($snipeSettings) && ($snipeSettings->favicon!='') ? Storage::disk('public')->url(e($snipeSettings->favicon)) : config('app.url').'/favicon.ico' }} ">
|
||||
|
||||
|
||||
|
|
|
@ -327,8 +327,8 @@
|
|||
item_icon = '';
|
||||
}
|
||||
|
||||
// display the username if it's checked out to a user
|
||||
if (value.username) {
|
||||
// display the username if it's checked out to a user, but don't do it if the username's there already
|
||||
if (value.username && !value.name.match('\\(') && !value.name.match('\\)')) {
|
||||
value.name = value.name + ' (' + value.username + ')';
|
||||
}
|
||||
|
||||
|
|
|
@ -869,16 +869,19 @@
|
|||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
@if ( Helper::checkUploadIsImage($file->get_src('users')))
|
||||
@if ((Storage::exists('private_uploads/users/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('users'))))
|
||||
<a href="{{ route('show/userfile', ['userId' => $user->id, 'fileId' => $file->id, 'download' => 'false']) }}" data-toggle="lightbox" data-type="image"><img src="{{ route('show/userfile', ['userId' => $user->id, 'fileId' => $file->id]) }}" class="img-thumbnail" style="max-width: 50px;"></a>
|
||||
@else
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
{{ trans('general.file_not_found') }}
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
{{ $file->filename }}
|
||||
</td>
|
||||
<td>
|
||||
{{ Helper::formatFilesizeUnits(Storage::size('private_uploads/users/'.$file->filename)) }}
|
||||
<td data-value="{{ (Storage::exists('private_uploads/users/'.$file->filename)) ? Storage::size('private_uploads/users/'.$file->filename) : '' }}">
|
||||
{{ (Storage::exists('private_uploads/users/'.$file->filename)) ? Helper::formatFilesizeUnits(Storage::size('private_uploads/users/'.$file->filename)) : '' }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
@ -888,10 +891,12 @@
|
|||
</td>
|
||||
<td>
|
||||
@if ($file->filename)
|
||||
<a href="{{ route('show/userfile', [$user->id, $file->id]) }}" class="btn btn-default">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
@if ((Storage::exists('private_uploads/users/'.$file->filename)) && ( Helper::checkUploadIsImage($file->get_src('users'))))
|
||||
<a href="{{ route('show/userfile', [$user->id, $file->id]) }}" class="btn btn-default">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ trans('general.download') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $file->created_at }}</td>
|
||||
|
@ -949,7 +954,8 @@
|
|||
@endif
|
||||
<th class="col-sm-3" data-field="item.serial" data-visible="false">{{ trans('admin/hardware/table.serial') }}</th>
|
||||
<th class="col-sm-3" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th class="col-sm-2" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
<th class="col-sm-3" data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th class="col-sm-2" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Tests\Browser;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
|
@ -26,6 +26,9 @@ class LoginTest extends DuskTestCase
|
|||
$user->permissions = '{"superuser": 1}';
|
||||
|
||||
$user->save();
|
||||
|
||||
Setting::factory()->create();
|
||||
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->visitRoute('login')
|
||||
->assertSee(trans('auth/general.login_prompt'));
|
||||
|
@ -37,10 +40,7 @@ class LoginTest extends DuskTestCase
|
|||
->type('password', 'password')
|
||||
->press(trans('auth/general.login'))
|
||||
->assertPathIs('/');
|
||||
$browser->screenshot('dashboard');
|
||||
$browser->screenshot('dashboard');
|
||||
});
|
||||
|
||||
// Delete the user afterwards
|
||||
$user->delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ namespace Tests;
|
|||
use Facebook\WebDriver\Chrome\ChromeOptions;
|
||||
use Facebook\WebDriver\Remote\DesiredCapabilities;
|
||||
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Laravel\Dusk\TestCase as BaseTestCase;
|
||||
|
||||
abstract class DuskTestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
use DatabaseMigrations;
|
||||
|
||||
/**
|
||||
* Prepare for Dusk test execution.
|
||||
|
|
Loading…
Reference in a new issue