Merge remote-tracking branch 'origin/develop'

This commit is contained in:
snipe 2024-10-22 15:39:20 +01:00
commit 03b01689b5
14 changed files with 73 additions and 182 deletions

View file

@ -76,4 +76,4 @@ jobs:
DB_DATABASE: snipeit
DB_PORT: ${{ job.services.mysql.ports[3306] }}
DB_USERNAME: root
run: php artisan test --parallel
run: php artisan test

View file

@ -74,4 +74,4 @@ jobs:
DB_PORT: ${{ job.services.postgresql.ports[5432] }}
DB_USERNAME: snipeit
DB_PASSWORD: password
run: php artisan test --parallel
run: php artisan test

View file

@ -58,4 +58,4 @@ jobs:
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite_testing
run: php artisan test --parallel
run: php artisan test

View file

@ -14,6 +14,7 @@ use App\Http\Transformers\UsersTransformer;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\License;
use App\Models\User;
@ -371,6 +372,7 @@ class UsersController extends Controller
$user = new User;
$user->fill($request->all());
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$user->created_by = auth()->id();
if ($request->has('permissions')) {
@ -452,6 +454,10 @@ class UsersController extends Controller
$user->fill($request->all());
if ($request->filled('company_id')) {
$user->company_id = Company::getIdForCurrentUser($request->input('company_id'));
}
if ($user->id == $request->input('manager_id')) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}

View file

@ -52,6 +52,10 @@ class BulkAssetsController extends Controller
}
$asset_ids = $request->input('ids');
if ($request->input('bulk_actions') === 'checkout') {
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('hardware.bulkcheckout.show');
}
// Figure out where we need to send the user after the update is complete, and store that in the session
$bulk_back_url = request()->headers->get('referer');
@ -571,31 +575,34 @@ class BulkAssetsController extends Controller
}
$errors = [];
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, $errors, $asset_ids, $request) {
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference!
foreach ($asset_ids as $asset_id) {
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkout', $asset);
$error = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
//TODO - I think this logic is duplicated in the checkOut method?
if ($target->location_id != '') {
$asset->location_id = $target->location_id;
$asset->unsetEventDispatcher();
$asset->save();
// TODO - I don't know why this is being saved without events
$asset::withoutEvents(function () use ($asset) {
$asset->save();
});
}
if ($error) {
array_merge_recursive($errors, $asset->getErrors()->toArray());
if (!$checkout_success) {
$errors = array_merge_recursive($errors, $asset->getErrors()->toArray());
}
}
});
if (! $errors) {
// Redirect to the new asset page
return redirect()->to('hardware')->with('success', trans('admin/hardware/message.checkout.success'));
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.bulkcheckout.show')->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors);
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors);
} catch (ModelNotFoundException $e) {
return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors());
}

View file

@ -141,6 +141,8 @@ class ActionlogsTransformer
if ($actionlog->item) {
if ($actionlog->itemType() == 'asset') {
$file_url = route('show/assetfile', ['assetId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'accessory') {
$file_url = route('show.accessoryfile', ['accessoryId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'license') {
$file_url = route('show.licensefile', ['licenseId' => $actionlog->item->id, 'fileId' => $actionlog->id]);
} elseif ($actionlog->itemType() == 'user') {
@ -346,4 +348,4 @@ class ActionlogsTransformer
}
}

View file

@ -8,9 +8,6 @@ trait CompanyableTrait
* This trait is used to scope models to the current company. To use this scope on companyable models,
* we use the "use Companyable;" statement at the top of the mode.
*
* We CANNOT USE THIS ON USERS, as it causes an infinite loop and prevents users from logging in, since this scope will be
* applied to the currently logged in (or logging in) user in addition to the user model for viewing lists of users.
*
* @see \App\Models\Company\Company::scopeCompanyables()
* @return void
*/

View file

@ -74,7 +74,6 @@
"ext-exif": "*"
},
"require-dev": {
"brianium/paratest": "^7.0",
"fakerphp/faker": "^1.16",
"larastan/larastan": "^2.9",
"mockery/mockery": "^1.4",

156
composer.lock generated
View file

@ -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": "3819ab4ef72eb77fabe494c0e746b83b",
"content-hash": "5341bc5be02b3c33e28e46e06dd99f29",
"packages": [
{
"name": "alek13/slack",
@ -11790,101 +11790,6 @@
],
"time": "2024-04-13T18:00:56+00:00"
},
{
"name": "brianium/paratest",
"version": "v7.3.1",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "551f46f52a93177d873f3be08a1649ae886b4a30"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30",
"reference": "551f46f52a93177d873f3be08a1649ae886b4a30",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-simplexml": "*",
"fidry/cpu-core-counter": "^0.5.1 || ^1.0.0",
"jean85/pretty-package-versions": "^2.0.5",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"phpunit/php-code-coverage": "^10.1.7",
"phpunit/php-file-iterator": "^4.1.0",
"phpunit/php-timer": "^6.0",
"phpunit/phpunit": "^10.4.2",
"sebastian/environment": "^6.0.1",
"symfony/console": "^6.3.4 || ^7.0.0",
"symfony/process": "^6.3.4 || ^7.0.0"
},
"require-dev": {
"doctrine/coding-standard": "^12.0.0",
"ext-pcov": "*",
"ext-posix": "*",
"infection/infection": "^0.27.6",
"phpstan/phpstan": "^1.10.40",
"phpstan/phpstan-deprecation-rules": "^1.1.4",
"phpstan/phpstan-phpunit": "^1.3.15",
"phpstan/phpstan-strict-rules": "^1.5.2",
"squizlabs/php_codesniffer": "^3.7.2",
"symfony/filesystem": "^6.3.1 || ^7.0.0"
},
"bin": [
"bin/paratest",
"bin/paratest.bat",
"bin/paratest_for_phpstorm"
],
"type": "library",
"autoload": {
"psr-4": {
"ParaTest\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brian Scaturro",
"email": "scaturrob@gmail.com",
"role": "Developer"
},
{
"name": "Filippo Tessarotto",
"email": "zoeslam@gmail.com",
"role": "Developer"
}
],
"description": "Parallel testing for PHP",
"homepage": "https://github.com/paratestphp/paratest",
"keywords": [
"concurrent",
"parallel",
"phpunit",
"testing"
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v7.3.1"
},
"funding": [
{
"url": "https://github.com/sponsors/Slamdunk",
"type": "github"
},
{
"url": "https://paypal.me/filippotessarotto",
"type": "paypal"
}
],
"time": "2023-10-31T09:24:17+00:00"
},
{
"name": "clue/ndjson-react",
"version": "v1.3.0",
@ -12781,65 +12686,6 @@
},
"time": "2020-07-09T08:09:16+00:00"
},
{
"name": "jean85/pretty-package-versions",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/Jean85/pretty-package-versions.git",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0.0",
"php": "^7.1|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"jean85/composer-provided-replaced-stub-package": "^1.0",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^7.5|^8.5|^9.4",
"vimeo/psalm": "^4.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Jean85\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alessandro Lai",
"email": "alessandro.lai85@gmail.com"
}
],
"description": "A library to get pretty versions strings of installed dependencies",
"keywords": [
"composer",
"package",
"release",
"versions"
],
"support": {
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
},
"time": "2024-03-08T09:58:59+00:00"
},
{
"name": "justinrainbow/json-schema",
"version": "5.3.0",

View file

@ -79,6 +79,11 @@ return [
'no_assets_selected' => 'You must select at least one asset from the list',
],
'multi-checkout' => [
'error' => 'Asset was not checked out, please try again|Assets were not checked out, please try again',
'success' => 'Asset checked out successfully.|Assets checked out successfully.',
],
'checkin' => [
'error' => 'Asset was not checked in, please try again',
'success' => 'Asset checked in successfully.',

View file

@ -194,7 +194,6 @@
@foreach ($accessory->uploads as $file)
<tr>
<td>
<x-icon type="paperclip" class="fa-2x" />
<i class="{{ Helper::filetype_icon($file->filename) }} icon-med" aria-hidden="true"></i>
<span class="sr-only">{{ Helper::filetype_icon($file->filename) }}</span>

View file

@ -115,5 +115,12 @@
@section('moar_scripts')
@include('partials/assets-assigned')
<script nonce="{{ csrf_token() }}">
$(function () {
//if there's already a user selected, make sure their checked-out assets show up
// (if there isn't one, it won't do anything)
$('#assigned_user').change();
});
</script>
@stop

View file

@ -16,17 +16,20 @@
</label>
<select name="bulk_actions" class="form-control select2" aria-label="bulk_actions" style="min-width: 350px;">
@if((isset($status)) && ($status == 'Deleted'))
@can('delete', \App\Models\Asset::class)
<option value="restore">{{trans('button.restore')}}</option>
@endcan
@can('delete', \App\Models\Asset::class)
<option value="restore">{{trans('button.restore')}}</option>
@endcan
@else
@can('update', \App\Models\Asset::class)
<option value="edit">{{ trans('button.edit') }}</option>
@endcan
@can('delete', \App\Models\Asset::class)
<option value="delete">{{ trans('button.delete') }}</option>
@endcan
<option value="labels" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=l" : ''}}>{{ trans_choice('button.generate_labels', 2) }}</option>
@can('update', \App\Models\Asset::class)
<option value="edit">{{ trans('button.edit') }}</option>
@endcan
@can('checkout', \App\Models\Asset::class)
<option value="checkout">{{ trans('general.bulk_checkout') }}</option>
@endcan
@can('delete', \App\Models\Asset::class)
<option value="delete">{{ trans('button.delete') }}</option>
@endcan
<option value="labels" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=l" : ''}}>{{ trans_choice('button.generate_labels', 2) }}</option>
@endif
</select>

View file

@ -0,0 +1,20 @@
<?php
namespace Tests\Unit\BladeComponents;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
use Tests\TestCase;
class IconComponentTest extends TestCase
{
public function testIconComponentDoesNotEndInNewline()
{
$renderedTemplateString = View::make('blade.icon', ['type' => 'checkout'])->render();
$this->assertFalse(
Str::endsWith($renderedTemplateString, PHP_EOL),
'Newline found at end of icon component. Bootstrap tables will not render if there is a newline at the end of the file.'
);
}
}