Compare commits

..

98 Commits

Author SHA1 Message Date
snipe
f98f978502 Fixed typos
Signed-off-by: snipe <snipe@snipe.net>
2024-12-11 16:53:15 +00:00
snipe
a0bbafeb30 Removed unused code
Signed-off-by: snipe <snipe@snipe.net>
2024-12-10 14:35:22 +00:00
snipe
dbd33e1a34 Check for specific error message
Signed-off-by: snipe <snipe@snipe.net>
2024-12-10 14:34:16 +00:00
snipe
8afba32169 Validate group data
Signed-off-by: snipe <snipe@snipe.net>
2024-12-10 14:23:23 +00:00
snipe
5014a95d9a Updated supported versions
Signed-off-by: snipe <snipe@snipe.net>
2024-12-10 13:27:59 +00:00
snipe
5f0efd8cdb Merge pull request #15940 from snipe/reove_gh_templates 2024-12-09 19:35:31 +00:00
snipe
31155b5351 Removed issue/PR templates
Signed-off-by: snipe <snipe@snipe.net>
2024-12-09 19:28:51 +00:00
snipe
cc8f72c3f9 Merge pull request #15914 from Godmartinz/send_reminder_emails 2024-12-09 19:23:13 +00:00
snipe
a0bab70def Merge pull request #15939 from uberbrady/fix_ldap_asset_location_updater 2024-12-09 17:56:23 +00:00
Brady Wetherington
b5c8251539 Only update asset locations to assets checked out to users. 2024-12-09 17:40:10 +00:00
snipe
0149ed75fd Merge pull request #15565 from spencerrlongg/bug/sc-25921 2024-12-09 17:00:10 +00:00
snipe
f72635955d Merge pull request #15925 from Godmartinz/refactor-unaccepted-assets-reminder-notif 2024-12-09 16:40:21 +00:00
Godfrey M
5120cddda4 delete notification version of reminder 2024-12-05 16:02:55 -08:00
Godfrey M
97398f1e68 adds testing to unaccepted reminder command 2024-12-05 12:41:33 -08:00
Godfrey M
281ff6ad5d wrap comment 2024-12-05 11:52:34 -08:00
Godfrey M
9d49b01958 cleans up the Unaccepted Asset reminder variables 2024-12-05 11:46:56 -08:00
Godfrey M
3f8916ea2e fix duplicate emails being sent in unaccepted reminder command 2024-12-05 11:37:03 -08:00
Godfrey M
f6b9ae6aee missed a spot 2024-12-05 10:18:35 -08:00
Godfrey M
de41def2b3 fixed conditionals 2024-12-05 10:15:57 -08:00
Godfrey M
52b051e940 add mail class for unaccepted assets reminders 2024-12-05 10:08:39 -08:00
snipe
a137e31797 Merge pull request #15920 from akemidx/asset-category-in-emails 2024-12-04 21:46:43 +00:00
akemidx
ca0f8ace99 fixed 2024-12-03 16:57:28 -05:00
akemidx
6252f0ac5e layout / added category id 2024-12-03 16:10:42 -05:00
snipe
43d66a8fcc Merge pull request #15918 from Godmartinz/mail_name_fix
Adds `MAIL_FROM_NAME` to mail envelope
2024-12-03 17:17:26 +00:00
Godfrey M
e5284c03e2 fix typo 2024-12-03 09:10:50 -08:00
Godfrey M
16283b8fc0 adds mail from name to mail envelope 2024-12-03 09:08:39 -08:00
Godfrey M
983b78edd9 add property check 2024-12-02 14:27:04 -08:00
snipe
2ad1407d51 Merge pull request #15909 from Godmartinz/checkin-out-notifications
Fixed general webhook option notifications not firing
2024-12-02 22:03:32 +00:00
Godfrey M
ab67c48352 fix unaccepted assets report resend acceptance 2024-12-02 13:03:09 -08:00
snipe
14730184c9 Fixed sr-only blank text
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 18:48:02 +00:00
snipe
8ff099e802 Merge pull request #15910 from marcusmoore/chore/include-testing-variable
Included MAIL_FROM_ADDR in phpunit configuration
2024-12-02 18:34:08 +00:00
Marcus Moore
ea1f8328b9 Include MAIL_FROM_ADDR in phpunit configuration 2024-12-02 10:23:02 -08:00
Godfrey M
99464fcd86 fixes general webhook not firing 2024-12-02 10:19:24 -08:00
snipe
93d4d24194 Fixed funky layout in importer
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:56:15 +00:00
snipe
1bbe9bf6c9 Merge remote-tracking branch 'origin/develop' 2024-12-02 17:43:03 +00:00
snipe
6874f703bf Some CSS twiddling
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:42:26 +00:00
snipe
a7d5b3944e Merge pull request #15908 from snipe/revert-15892-bug/harden-checkout-validation
Revert "Hardened asset checkout validation by requiring integer"
2024-12-02 17:42:02 +00:00
Marcus Moore
b5e83899c6 Revert "Hardened asset checkout validation by requiring integer" 2024-12-02 09:37:21 -08:00
snipe
dcd586e3cd Merge remote-tracking branch 'origin/develop' 2024-12-02 17:17:20 +00:00
snipe
1246ab1de7 Fixed bulk user form in user detail view
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 17:14:36 +00:00
snipe
5a4a3aa8f3 Merge remote-tracking branch 'origin/develop' 2024-12-02 15:59:25 +00:00
snipe
2220828b00 Merge pull request #15904 from snipe/fixes/#15901
Fixed #15901 - re-added required indicator on text and select custom fields
2024-12-02 15:51:32 +00:00
snipe
716c67d4b8 Fixed #15901 - re-added required indicator on text and select custom fields
Signed-off-by: snipe <snipe@snipe.net>
2024-12-02 15:45:07 +00:00
snipe
ee4a54be24 Fixed order by notes for users
Signed-off-by: snipe <snipe@snipe.net>
2024-11-27 15:48:13 +00:00
snipe
46be1ada60 Merge pull request #15892 from marcusmoore/bug/harden-checkout-validation
Hardened asset checkout validation by requiring integer
2024-11-27 14:55:11 +00:00
snipe
e1c0a80c20 Merge remote-tracking branch 'origin/develop' 2024-11-27 13:52:03 +00:00
snipe
2cb1b6d462 Use transformer on API update
Signed-off-by: snipe <snipe@snipe.net>
2024-11-27 13:51:53 +00:00
snipe
37ac7fe25c Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2024-11-26 20:38:46 +00:00
snipe
11f83b4cd9 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2024-11-26 20:12:14 +00:00
snipe
0f1d10bd69 Dev assets
Signed-off-by: snipe <snipe@snipe.net>
2024-11-26 20:12:09 +00:00
snipe
82108f8a18 Merge pull request #15208 from akemidx/feature/sc-26415
FEATURE: Option for Notes to be Required on Asset Checkin/Checkout
2024-11-26 20:11:04 +00:00
akemidx
a7dae10a82 fixing line 2024-11-26 15:06:25 -05:00
akemidx
2727210c78 requested changes from pr 2024-11-26 15:04:54 -05:00
Marcus Moore
fcbd5dcae5 Harden asset checkout validation by requiring ints 2024-11-26 11:33:17 -08:00
snipe
fa80716320 Merge pull request #15890 from Godmartinz/mobile-view-fix
Mobile view fix
2024-11-26 19:13:39 +00:00
Godfrey M
15c9df0ab1 fix mobile view for asset and user 2024-11-26 10:59:31 -08:00
snipe
6ee0c93c87 Merge remote-tracking branch 'origin/develop' 2024-11-26 18:47:22 +00:00
snipe
37e091adbb Merge pull request #15889 from Godmartinz/Fix-user-API-location-update
Fixed Users API `update` from clearing `location_id` unnecessarily
2024-11-26 18:45:20 +00:00
Godfrey M
021e82927e fixed mobile view of assets 2024-11-26 10:42:59 -08:00
Godfrey M
60642cd902 fix user api update from clearing location_id unnecessarily 2024-11-26 09:58:17 -08:00
akemidx
0d608552ef orange bar for requirednessness 2024-11-21 19:09:54 -05:00
akemidx
dd223fc215 commit for testing 2024-11-21 19:01:27 -05:00
akemidx
9cb411c500 removing required form div container 2024-11-13 18:06:04 -05:00
akemidx
f0d3a6e2d3 removing some comments/merging in develop 2024-10-16 18:35:31 -04:00
akemidx
5cb940c2ee Merge branch 'refs/heads/upstream/dev' into feature/sc-26415 2024-10-16 18:32:36 -04:00
akemidx
299e743848 weird requesting 2024-10-08 15:51:53 -04:00
akemidx
0c84904bf9 un'required'ing the rule. not sure what's breaking here but looking 2024-10-02 18:56:41 -04:00
akemidx
e00a1aec02 note box is now missing when unchecking setting 2024-10-02 18:52:33 -04:00
akemidx
06e3bb7fd1 requested changes 2024-10-02 18:37:11 -04:00
akemidx
492e686b7a Merge remote-tracking branch 'origin/feature/sc-26415' into feature/sc-26415
# Conflicts:
#	app/Http/Controllers/Assets/AssetCheckinController.php
#	app/Http/Controllers/Assets/AssetCheckoutController.php
#	app/Http/Requests/AssetCheckinRequest.php
2024-10-02 18:17:38 -04:00
akemidx
17706f150e requested changes 2024-10-02 18:15:53 -04:00
akemidx
6fef127cd1 missing closing ) 2024-10-02 18:15:53 -04:00
akemidx
f45b836010 backend form validation. +cleanup 2024-10-02 18:15:53 -04:00
akemidx
925aea8531 back to having tests pass. needed to comment out the notes rules() i added 2024-10-02 18:15:53 -04:00
akemidx
bd6698de2a fixing some formatting 2024-10-02 18:15:53 -04:00
akemidx
515f59fed9 more test work 2024-10-02 18:15:52 -04:00
akemidx
5e74b109d9 front end changes/updates from gh 2024-10-02 18:15:52 -04:00
akemidx
8b643cb3b9 note field optional 2024-10-02 18:15:52 -04:00
akemidx
700647c53f setting created 2024-10-02 18:15:52 -04:00
akemidx
081c5706c4 required, but not optional 2024-10-02 18:15:52 -04:00
akemidx
73a059c9ac missing closing ) 2024-10-01 16:37:30 -04:00
spencerrlongg
4f9f035c69 added a way to manipulate validator attribute names 2024-09-30 20:09:00 -05:00
spencerrlongg
cd3059f790 rm numbers too. :( 2024-09-30 13:38:52 -05:00
spencerrlongg
b69596a261 rm dump 2024-09-25 16:21:33 -05:00
spencerrlongg
d02b9933e5 rm comment 2024-09-25 15:38:40 -05:00
spencerrlongg
852a56fa9b should be all good now 2024-09-25 15:30:41 -05:00
spencerrlongg
9df78a9ed0 working! need formatting for error messages etc 2024-09-25 14:00:50 -05:00
spencerrlongg
f3ad89931f hm ok this kind of works 2024-09-25 13:00:35 -05:00
spencerrlongg
21906d8c27 world set up and idea in place 2024-09-25 12:47:56 -05:00
akemidx
d262638a63 backend form validation. +cleanup 2024-09-04 18:23:36 -04:00
akemidx
bebb72a04f back to having tests pass. needed to comment out the notes rules() i added 2024-09-04 17:53:19 -04:00
akemidx
77c5035cac fixing some formatting 2024-09-04 17:50:52 -04:00
akemidx
2901ecbf43 more test work 2024-09-03 15:02:27 -04:00
akemidx
27c120a55e front end changes/updates from gh 2024-08-20 19:07:47 -04:00
akemidx
4e43fa6b9f Merge remote-tracking branch 'upstream/develop' into feature/sc-26415 2024-08-19 21:06:18 -04:00
akemidx
0f0baa207d note field optional 2024-08-01 17:02:35 -04:00
akemidx
3ff1745f56 setting created 2024-08-01 16:44:02 -04:00
akemidx
552f90ae2c required, but not optional 2024-08-01 16:06:47 -04:00
53 changed files with 452 additions and 464 deletions

View File

@@ -17,5 +17,3 @@ DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
DB_PASSWORD=null
MAIL_FROM_ADDR=you@example.com

View File

@@ -1,38 +0,0 @@
#### Expected Behavior (or desired behavior if a feature request)
(what you expect to happen goes here)
-----
#### Actual Behavior
(what actually happens goes here)
-----
#### Please confirm you have done the following before posting your bug report:
- [ ] I have enabled debug mode
- [ ] I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
-----
#### Provide answers to these questions:
- Is this a fresh install or an upgrade?
- Version of Snipe-IT you're running
- Version of PHP you're running
- Version of MySQL/MariaDB you're running
- What OS and web server you're running Snipe-IT on
- What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
- WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
- What specific Snipe-IT page you're on, and what specific element you're interacting with to trigger the error
- If a stacktrace is provided in the error, include that too.
- Any errors that appear in your browser's error console.
- Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
- Include any additional information you can find in `storage/logs` and your webserver's logs.
- Include what you've done so far in the installation, and if you got any error messages along the way.
- Indicate whether or not you've manually edited any data directly in the database
Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.
https://snipe-it.readme.io/docs/getting-help

View File

@@ -1,129 +0,0 @@
name: Bug Report
description: Create a report to help us improve
body:
- type: checkboxes
attributes:
label: Debug mode
description: Please confirm you have done the following before posting your bug report
options:
- label: I have enabled debug mode
required: true
- label: I have read [checked the Common Issues page](https://snipe-it.readme.io/docs/common-issues)
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Reproduction steps
description: Steps to reproduce the behavior.
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Screenshots
description: 'If applicable, add screenshots to help explain your problem.'
- type: markdown
attributes:
value: "### Server"
- type: input
attributes:
label: Snipe-IT Version
validations:
required: true
- type: input
id: server_operatingSystem
attributes:
label: Operating System
description: 'e.g. Ubuntu, Windows'
validations:
required: true
- type: input
attributes:
label: Web Server
description: 'e.g. Apache, IIS'
validations:
required: true
- type: input
attributes:
label: PHP Version
validations:
required: true
- type: markdown
attributes:
value: "### Desktop"
- type: input
id: desktop_operatingSystem
attributes:
label: Operating System
description: 'e.g. Ubuntu, Windows'
- type: input
id: desktop_browser
attributes:
label: Browser
description: 'e.g. Google Chrome, Safari'
- type: input
id: desktop_version
attributes:
label: Version
description: 'e.g. 93'
- type: markdown
attributes:
value: "### Mobile"
- type: input
attributes:
label: Device
description: 'e.g. iPhone 6, Pixel 4a'
- type: input
id: mobile_operatingSystem
attributes:
label: Operating System
description: 'e.g. iOS 8.1, Android 9'
- type: input
id: mobile_browser
attributes:
label: Browser
description: 'e.g. Google Chrome, Safari'
- type: input
id: mobile_version
attributes:
label: Version
description: 'e.g. 93'
- type: textarea
attributes:
label: Error messages
description: |
WITH DEBUG TURNED ON, if you're getting an error in your browser, include that error
If a stacktrace is provided in the error, include that too.
Any errors that appear in your browser's error console.
Confirm whether the error is reproducible on the demo: https://snipeitapp.com/demo.
Include any additional information you can find in `storage/logs` and your webserver's logs.
Include the output from `php -m` (this should display what modules you have enabled.)
render: shell
- type: textarea
attributes:
label: Additional context
description: |
Is this a fresh install or an upgrade?
What OS and web server you're running Snipe-IT on
What method you used to install Snipe-IT (install.sh, manual installation, docker, etc)
Include what you've done so far in the installation, and if you got any error messages along the way.
Indicate whether or not you've manually edited any data directly in the database
Add any other context about the problem here.
- type: markdown
attributes:
value: Please do not post an issue without answering the related questions above. If you have opened a different issue and already answered these questions, answer them again, once for every ticket. It will be next to impossible for us to help you.

View File

@@ -1 +0,0 @@
blank_issues_enabled: false

View File

@@ -1,25 +0,0 @@
name: Feature Request
description: Suggest an idea for this project
title: "[Feature Request]: "
labels: ["feature request"]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. The more information you can provide about your use-case, the more liklely we are to consider your feature.
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

View File

@@ -1,40 +0,0 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context, providing screenshots where practical. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B
**Test Configuration**:
* PHP version:
* MySQL version
* Webserver version
* OS version
# Checklist:
- [ ] I have read the Contributing documentation available here: https://snipe-it.readme.io/docs/contributing-overview
- [ ] I have formatted this PR according to the project guidelines: https://snipe-it.readme.io/docs/contributing-overview#pull-request-guidelines
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

View File

@@ -1 +0,0 @@
memory_limit= 2048M

View File

@@ -1,7 +0,0 @@
# Configuration for weekly-digest - https://github.com/apps/weekly-digest
publishDay: sun
canPublishIssues: true
canPublishPullRequests: true
canPublishContributors: true
canPublishStargazers: true
canPublishCommits: true

View File

@@ -10,10 +10,12 @@ however there are times when library dependencies and/or PHP/MySQL dependencies
make it impossible to backport security fixes on older versions.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
|---------| ------------------ |
| 7.x | :white_check_mark: |
| 6.x | :x: |
| 5.1.x | :x: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| 4.0.x | :x: |
| < 4.0 | :x: |
## Reporting a Vulnerability

View File

@@ -427,7 +427,7 @@ class LdapSync extends Command
$user->groups()->attach($ldap_default_group);
}
//updates assets location based on user's location
Asset::where('assigned_to', '=', $user->id)->update(['location_id' => $user->location_id]);
Asset::where('assigned_to', '=', $user->id)->where('assigned_type', '=', User::class)->update(['location_id' => $user->location_id]);
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {

View File

@@ -2,15 +2,15 @@
namespace App\Console\Commands;
use App\Mail\UnacceptedAssetReminderMail;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CheckoutAssetNotification;
use App\Notifications\CurrentInventory;
use App\Notifications\UnacceptedAssetReminderNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Mail;
class SendAcceptanceReminder extends Command
{
@@ -65,42 +65,29 @@ class SendAcceptanceReminder extends Command
return $item['acceptance']->assignedTo ? $item['acceptance']->assignedTo->id : '';
});
$no_mail_address = [];
foreach($unacceptedAssetGroups as $unacceptedAssetGroup) {
// The [0] is weird, but it allows for the item_count to work and grabs the appropriate info for each user.
// Collapsing and flattening the collection doesn't work above.
$acceptance = $unacceptedAssetGroup[0]['acceptance'];
$locale = $acceptance->assignedTo?->locale;
$email = $acceptance->assignedTo?->email;
if(!$email){
$this->info($acceptance->assignedTo->present()->fullName().' has no email address.');
}
$item_count = $unacceptedAssetGroup->count();
foreach ($unacceptedAssetGroup as $unacceptedAsset) {
// if ($unacceptedAsset['acceptance']->assignedTo->email == ''){
// $no_mail_address[] = $unacceptedAsset['checkoutable']->assignedTo->present()->fullName;
// }
if ($unacceptedAsset['acceptance']->assignedTo) {
if (!$unacceptedAsset['acceptance']->assignedTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
$unacceptedAsset['acceptance']->assignedTo,
new UnacceptedAssetReminderNotification($unacceptedAsset['assetItem'], $count)
);
} else {
Notification::send(
$unacceptedAsset['acceptance']->assignedTo,
new UnacceptedAssetReminderNotification($unacceptedAsset, $item_count)
);
}
$count++;
}
if ($locale && $email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count))->locale($locale));
} elseif ($email) {
Mail::to($email)->send((new UnacceptedAssetReminderMail($acceptance, $item_count)));
}
$count++;
}
if (!empty($no_mail_address)) {
foreach($no_mail_address as $user) {
return $user.' has no email.';
}
}
$this->info($count.' users notified.');
return 0;
}
}

View File

@@ -765,9 +765,9 @@ class AssetsController extends Controller
}
if ($problems_updating_encrypted_custom_fields) {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.encrypted_warning')));
} else {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.update.success')));
}
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);

View File

@@ -283,6 +283,7 @@ class UsersController extends Controller
'autoassign_licenses',
'website',
'locale',
'notes',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
@@ -480,10 +481,11 @@ class UsersController extends Controller
$user->permissions = $permissions_array;
}
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
if($request->has('location_id')) {
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)->update(['location_id' => $request->input('location_id', null)]);
}
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
if ($user->save()) {

View File

@@ -18,6 +18,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\MessageBag;
/**
@@ -29,6 +30,7 @@ use \Illuminate\Http\RedirectResponse;
*/
class AssetModelsController extends Controller
{
protected MessageBag $validatorErrors;
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the accessories listing, which is generated in getDatatable.
@@ -158,7 +160,7 @@ class AssetModelsController extends Controller
if ($this->shouldAddDefaultValues($request->input())) {
if (!$this->assignCustomFieldsDefaultValues($model, $request->input('default_values'))) {
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.fieldset_default_value.error'));
return redirect()->back()->withInput()->withErrors($this->validatorErrors);
}
}
@@ -481,9 +483,15 @@ class AssetModelsController extends Controller
$rules[$field] = $validation;
}
$validator = Validator::make($data, $rules);
$attributes = [];
foreach ($model->fieldset->fields as $field) {
$attributes[$field->db_column] = trim(preg_replace('/_+|snipeit|\d+/', ' ', $field->db_column));
}
$validator = Validator::make($data, $rules)->setAttributeNames($attributes);
if($validator->fails()){
$this->validatorErrors = $validator->errors();
return false;
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Mail\CheckoutAssetMail;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
@@ -18,6 +19,7 @@ use App\Notifications\CheckoutAssetNotification;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use \Illuminate\Contracts\View\View;
use League\Csv\Reader;
@@ -1150,24 +1152,17 @@ class ReportsController extends Controller
}
$logItem = $logItem_res[0];
}
$email = $assetItem->assignedTo?->email;
$locale = $assetItem->assignedTo?->locale;
// Only send notification if assigned
if ($assetItem->assignedTo) {
if ($locale && $email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $logItem->note, $acceptance))->locale($locale));
if (!$assetItem->assignedTo->locale) {
Notification::locale(Setting::getSettings()->locale)->send(
$assetItem->assignedTo,
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
);
} else {
Notification::send(
$assetItem->assignedTo,
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
);
} elseif ($email) {
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $logItem->note, $acceptance)));
}
}
if ($assetItem->assignedTo->email == ''){
if ($email == ''){
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
}

View File

@@ -334,6 +334,8 @@ class SettingsController extends Controller
$setting->depreciation_method = $request->input('depreciation_method');
$setting->dash_chart_type = $request->input('dash_chart_type');
$setting->profile_edit = $request->input('profile_edit', 0);
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
if ($request->input('per_page') != '') {
$setting->per_page = $request->input('per_page');

View File

@@ -21,9 +21,14 @@ class AssetCheckinRequest extends Request
*/
public function rules()
{
return [
$settings = \App\Models\Setting::getSettings();
];
$rules = [];
if($settings->require_checkinout_notes) {
$rules['note'] = 'string|required';
}
return $rules;
}
public function response(array $errors)

View File

@@ -21,6 +21,8 @@ class AssetCheckoutRequest extends Request
*/
public function rules()
{
$settings = \App\Models\Setting::getSettings();
$rules = [
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
@@ -35,7 +37,11 @@ class AssetCheckoutRequest extends Request
'nullable',
'date'
],
];
];
if($settings->require_checkinout_notes) {
$rules['note'] = 'required|string';
}
return $rules;
}

View File

@@ -7,6 +7,7 @@ use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use App\Rules\UserCannotSwitchCompaniesIfItemsAssigned;
use Illuminate\Support\Facades\Gate;
class SaveUserRequest extends FormRequest
{
@@ -17,7 +18,7 @@ class SaveUserRequest extends FormRequest
*/
public function authorize()
{
return true;
return (Gate::allows('users.create') || Gate::allows('users.edit'));
}
public function response(array $errors)
@@ -35,7 +36,8 @@ class SaveUserRequest extends FormRequest
$rules = [
'department_id' => 'nullable|exists:departments,id',
'manager_id' => 'nullable|exists:users,id',
'company_id' => ['nullable','exists:companies,id']
'company_id' => ['nullable','exists:companies,id'],
'groups' => ['nullable','exists:permission_groups,id']
];
switch ($this->method()) {

View File

@@ -100,7 +100,7 @@ class CheckoutableListener
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckoutNotification($event, $acceptance));
}
}
@@ -182,7 +182,7 @@ class CheckoutableListener
$notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint);
$notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams
} else {
Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint)
Notification::route($this->webhookSelected(), Setting::getSettings()->webhook_endpoint)
->notify($this->getCheckinNotification($event));
}
}
@@ -310,6 +310,13 @@ class CheckoutableListener
return $event->checkedOutTo?->email ?? '';
}
}
private function webhookSelected(){
if(Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general'){
return 'slack';
}
return Setting::getSettings()->webhook_selected;
}
/**
* Register the listeners for the subscriber.

View File

@@ -24,7 +24,15 @@ class CustomFieldSetDefaultValuesForModel extends Component
$this->fieldset_id = $this->model?->fieldset_id;
$this->add_default_values = ($this->model?->defaultValues->count() > 0);
$this->initializeSelectedValuesArray();
if (session()->has('errors')) {
$errors = session('errors')->keys();
$selectedValuesKeys = array_keys($this->selectedValues);
if (count(array_intersect($selectedValuesKeys, $errors)) > 0) {
$this->add_default_values = true;
};
}
$this->populatedSelectedValuesArray();
}

View File

@@ -34,7 +34,7 @@ class CheckinAccessoryMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -43,7 +43,7 @@ class CheckinAssetMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -34,7 +34,7 @@ class CheckinLicenseMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -37,7 +37,7 @@ class CheckoutAccessoryMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -52,7 +52,7 @@ class CheckoutAssetMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -38,7 +38,7 @@ class CheckoutConsumableMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -36,7 +36,7 @@ class CheckoutLicenseMail extends Mailable
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'));
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class UnacceptedAssetReminderMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct($checkout_info, $count)
{
$this->count = $count;
$this->target = $checkout_info['acceptance']?->assignedTo;
$this->acceptance = $checkout_info['acceptance'];
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$from = new Address(config('mail.from.address'), config('mail.from.name'));
return new Envelope(
from: $from,
subject: trans('mail.unaccepted_asset_reminder'),
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$accept_url = route('account.accept');
return new Content(
markdown: 'notifications.markdown.asset-reminder',
with: [
'count' => $this->count,
'assigned_to' => $this->target?->present()->fullName,
'link' => route('account.accept'),
'accept_url' => $accept_url,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -70,6 +70,7 @@ class Setting extends Model
protected $casts = [
'label2_asset_logo' => 'boolean',
'require_checkinout_notes' => 'boolean',
];
/**

View File

@@ -1,73 +0,0 @@
<?php
namespace App\Notifications;
use App\Models\Asset;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class UnacceptedAssetReminderNotification extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($checkout_info, $count)
{
$this->count = $count;
$this->target = $checkout_info['acceptance']->assignedTo;
$this->acceptance = $checkout_info['acceptance'];
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via()
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail()
{
$accept_url = route('account.accept');
$message = (new MailMessage)->markdown('notifications.markdown.asset-reminder',
[
'count' => $this->count,
'assigned_to' => $this->target->present()->fullName,
'link' => route('account.accept'),
'accept_url' => $accept_url,
])
->subject(trans('mail.unaccepted_asset_reminder'));
return $message;
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

View File

@@ -23,7 +23,7 @@ use Illuminate\Auth\Access\HandlesAuthorization;
abstract class SnipePermissionsPolicy
{
/**
* This should return the key of the model in the users json permission string.
* This should return the key of the model in the user's JSON permission string.
*
* @return bool
*/
@@ -37,11 +37,7 @@ abstract class SnipePermissionsPolicy
{
/**
* If an admin, they can do all item related tasks, but ARE constrained by FMCSA company access.
* That scoping happens on the model level (except for the Users model) via the Companyable trait.
*
* This does lead to some inconsistencies in the responses, since attempting to edit assets,
* accessories, etc (anything other than users) will result in a Forbidden error, whereas the users
* area will redirect with "That user doesn't exist" since the scoping is handled directly on those queries.
* That scoping happens on the model level via the Companyable trait.
*
* The *superuser* global permission gets handled in the AuthServiceProvider before() method.
*
@@ -53,7 +49,7 @@ abstract class SnipePermissionsPolicy
}
/**
* If we got here by $this→authorize('something', $actualModel) then we can continue on Il but if we got here
* If we got here by $this→authorize('something', $actualModel) then we can continue on, but if we got here
* via $this→authorize('something', Model::class) then calling Company:: isCurrentUserHasAccess($item) gets weird.
* Bail out here by returning "nothing" and allow the relevant method lower in this class to be called and handle authorization.
*/
@@ -85,7 +81,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can view the accessory.
* Determine whether the user can view the item.
*
* @param \App\Models\User $user
* @return mixed
@@ -112,7 +108,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can update the accessory.
* Determine whether the user can update the item.
*
* @param \App\Models\User $user
* @return mixed
@@ -124,7 +120,7 @@ abstract class SnipePermissionsPolicy
/**
* Determine whether the user can update the accessory.
* Determine whether the user can checkout the item.
*
* @param \App\Models\User $user
* @return mixed
@@ -135,7 +131,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can delete the accessory.
* Determine whether the user can delete the item.
*
* @param \App\Models\User $user
* @return mixed
@@ -151,7 +147,7 @@ abstract class SnipePermissionsPolicy
}
/**
* Determine whether the user can manage the accessory.
* Determine whether the user can manage the item.
*
* @param \App\Models\User $user
* @return mixed

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('require_checkinout_notes')->nullable()->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('settings', function (Blueprint $table) {
if (Schema::hasColumn('settings', 'require_checkinout_notes')) {
$table->dropColumn('require_checkinout_notes');
}
});
}
};

View File

@@ -21,6 +21,7 @@
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="MAIL_FROM_ADDR" value="app@example.com"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,8 +2,8 @@
"/js/build/app.js": "/js/build/app.js?id=5572f3bd32a6131651ab3022edd76941",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=d34ae2483cbe2c77478c45f4006eba55",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=b1c78591f51b52beab05b52f407ad6e6",
"/css/build/overrides.css": "/css/build/overrides.css?id=49a09bf67bd6129ef4ce910d0be09757",
"/css/build/app.css": "/css/build/app.css?id=6b9acfefe41b964aa29d58dd26d6f910",
"/css/build/overrides.css": "/css/build/overrides.css?id=b1146d30456ed95c3d4a9b60ddc58313",
"/css/build/app.css": "/css/build/app.css?id=e5f692af9dd2c217455ad87e520ef32a",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=53edc92eb2d272744bc7404ec259930e",
@@ -19,7 +19,7 @@
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=18787b3f00a3be7be38ee4e26cbd2a07",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/all.css": "/css/dist/all.css?id=9a85a7206ecd9a5e74ec0736bec0026e",
"/css/dist/all.css": "/css/dist/all.css?id=fa28de72acfa72bfdd7ad4206a65d4eb",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",

View File

@@ -384,7 +384,7 @@ a.logo.no-hover a:hover {
background-color: transparent;
}
input:required, select:required {
input:required, select:required, textarea:required {
border-right: 6px solid orange;
}

View File

@@ -876,7 +876,6 @@ th.css-component > .th-inner::before
}
@media screen and (max-width: 992px){
.info-stack-container {
display: flex;
flex-direction: column;
}
.col-md-3.col-xs-12.col-sm-push-9.info-stack{
@@ -892,6 +891,12 @@ th.css-component > .th-inner::before
float:none;
}
}
@media screen and (max-width: 992px){
.row-new-striped div{
width:100%;
}
}
@media screen and (max-width: 1318px) and (min-width: 1200px){
.admin.box{
height:170px;

View File

@@ -280,6 +280,8 @@ return [
'two_factor_enrollment_text' => "Two factor authentication is required, however your device has not been enrolled yet. Open your Google Authenticator app and scan the QR code below to enroll your device. Once you've enrolled your device, enter the code below",
'require_accept_signature' => 'Require Signature',
'require_accept_signature_help_text' => 'Enabling this feature will require users to physically sign off on accepting an asset.',
'require_checkinout_notes' => 'Require Notes on Checkin/Checkout',
'require_checkinout_notes_help_text' => 'Enabling this feature will require the note fields to be populated when checking in or checking out an asset.',
'left' => 'left',
'right' => 'right',
'top' => 'top',

View File

@@ -113,17 +113,17 @@
</div>
</div>
<!-- Note -->
<div class="form-group {{ $errors->has('note') ? 'error' : '' }}">
<label for="note" class="col-sm-3 control-label">
{{ trans('general.notes') }}
</label>
<div class="col-md-8">
<textarea class="col-md-6 form-control" id="note"
name="note">{{ old('note', $asset->note) }}</textarea>
{!! $errors->first('note', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
</div>
<!-- Note -->
<div class="form-group {{ $errors->has('note') ? 'error' : '' }}">
<label for="note" class="col-md-3 control-label">
{{ trans('general.notes') }}
</label>
<div class="col-md-8">
<textarea class="col-md-6 form-control" id="note" @required($snipeSettings->require_checkinout_notes)
name="note">{{ old('note', $asset->note) }}</textarea>
{!! $errors->first('note', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>
</div>
</div> <!--/.box-body-->
</div> <!--/.box-body-->

View File

@@ -141,8 +141,9 @@
<label for="note" class="col-md-3 control-label">
{{ trans('general.notes') }}
</label>
<div class="col-md-8">
<textarea class="col-md-6 form-control" id="note"
<textarea class="col-md-6 form-control" id="note" @required($snipeSettings->require_checkinout_notes)
name="note">{{ old('note', $asset->note) }}</textarea>
{!! $errors->first('note', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
</div>

View File

@@ -1,5 +1,4 @@
<span>
<div class="form-group{{ $errors->has('custom_fieldset') ? ' has-error' : '' }}">
<label for="custom_fieldset" class="col-md-3 control-label">
{{ trans('admin/models/general.fieldset') }}
@@ -11,6 +10,7 @@
<div class="col-md-3">
@if ($fieldset_id)
<label class="form-control">
{{ Form::checkbox('add_default_values', 1, old('add_default_values', $add_default_values), ['data-livewire-component' => $this->getId(), 'id' => 'add_default_values', 'wire:model.live' => 'add_default_values', 'disabled' => $this->fields->isEmpty()]) }}
{{ trans('admin/models/general.add_default_values') }}
</label>
@@ -19,12 +19,13 @@
</div>
@if ($add_default_values)
@if ($this->fields)
@if ($this->fields)
@foreach ($this->fields as $field)
<div class="form-group" wire:key="field-{{ $field->id }}">
<label class="col-md-3 control-label{{ $errors->has($field->name) ? ' has-error' : '' }}">{{ $field->name }}</label>
<label class="col-md-3 control-label{{ $errors->has($field->db_column_name()) ? ' has-error' : '' }}">{{ $field->name }}</label>
<div class="col-md-7">
@@ -123,10 +124,16 @@
Unknown field element: {{ $field->element }}
</span>
@endif
<?php
$errormessage = $errors->first($field->db_column_name());
if ($errormessage) {
print('<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> '.$errormessage.'</span>');
}
?>
</div>
</div>
@endforeach
@endforeach
@endif

View File

@@ -137,14 +137,16 @@
</button>
<a href="#" wire:click.prevent="$set('activeFileId',null)">
<button class="btn btn-sm btn-danger" wire:click="destroy({{ $currentFile->id }})">
<i class="fas fa-trash icon-white" aria-hidden="true"></i><span class="sr-only"></span></button>
<i class="fas fa-trash icon-white" aria-hidden="true"></i>
<span class="sr-only">{{ trans('general.delete') }}</span>
</button>
</a>
</td>
</tr>
@if( $currentFile && $this->activeFile && ($currentFile->id == $this->activeFile->id))
<tr class="warning">
<td colspan="4">
<td colspan="5">
<div class="form-group">

View File

@@ -2,20 +2,22 @@
@foreach($model->fieldset->fields AS $field)
<div class="form-group{{ $errors->has($field->db_column_name()) ? ' has-error' : '' }}">
<label for="{{ $field->db_column_name() }}" class="col-md-3 control-label">{{ $field->name }} </label>
<div class="col-md-7 col-sm-12{{ ($field->pivot->required=='1') ? ' required' : '' }}">
<div class="col-md-7 col-sm-12">
@if ($field->element!='text')
<!-- Listbox -->
@if ($field->element=='listbox')
<!-- Listbox -->
{{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(),
old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }}
old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class' => 'format select2 form-control', ($field->pivot->required=='1' ? ' required' : '') ]) }}
@elseif ($field->element=='textarea')
<textarea class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}">{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
<!-- Textarea -->
<textarea class="col-md-6 form-control" id="{{ $field->db_column_name() }}" name="{{ $field->db_column_name() }}"{{ ($field->pivot->required=='1') ? ' required' : '' }}>{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}</textarea>
@elseif ($field->element=='checkbox')
<!-- Checkboxes -->
<!-- Checkbox -->
@foreach ($field->formatFieldValuesAsArray() as $key => $value)
<div>
<label class="form-control">
@@ -26,27 +28,25 @@
@endforeach
@elseif ($field->element=='radio')
@foreach ($field->formatFieldValuesAsArray() as $value)
<div>
<label class="form-control">
<input type="radio" value="{{ $value }}" name="{{ $field->db_column_name() }}" {{ isset($item) ? ($item->{$field->db_column_name()} == $value ? ' checked="checked"' : '') : (old($field->db_column_name()) != '' ? ' checked="checked"' : (in_array($value, explode(', ', $field->defaultValue($model->id))) ? ' checked="checked"' : '')) }}>
{{ $value }}
</label>
</div>
@endforeach
<!-- Radio -->
@foreach ($field->formatFieldValuesAsArray() as $value)
<div>
<label class="form-control">
<input type="radio" value="{{ $value }}" name="{{ $field->db_column_name() }}" {{ isset($item) ? ($item->{$field->db_column_name()} == $value ? ' checked="checked"' : '') : (old($field->db_column_name()) != '' ? ' checked="checked"' : (in_array($value, explode(', ', $field->defaultValue($model->id))) ? ' checked="checked"' : '')) }}>
{{ $value }}
</label>
</div>
@endforeach
@endif
@else
<!-- Date field -->
@if ($field->format=='DATE')
<div class="input-group col-md-5" style="padding-left: 0px;">
<div class="input-group date" data-provide="datepicker" data-date-format="yyyy-mm-dd" data-autoclose="true" data-date-clear-btn="true">
<input type="text" class="form-control" placeholder="{{ trans('general.select_date') }}" name="{{ $field->db_column_name() }}" id="{{ $field->db_column_name() }}" readonly value="{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}" style="background-color:inherit">
<input type="text" class="form-control" placeholder="{{ trans('general.select_date') }}" name="{{ $field->db_column_name() }}" id="{{ $field->db_column_name() }}" readonly value="{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}" style="background-color:inherit"{{ ($field->pivot->required=='1') ? ' required' : '' }}>
<span class="input-group-addon"><x-icon type="calendar" /></span>
</div>
</div>
@@ -54,7 +54,7 @@
@else
@if (($field->field_encrypted=='0') || (Gate::allows('assets.view.encrypted_custom_fields')))
<input type="text" value="{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}" id="{{ $field->db_column_name() }}" class="form-control" name="{{ $field->db_column_name() }}" placeholder="Enter {{ strtolower($field->format) }} text">
<input type="text" value="{{ old($field->db_column_name(),(isset($item) ? Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))) }}" id="{{ $field->db_column_name() }}" class="form-control" name="{{ $field->db_column_name() }}" placeholder="Enter {{ strtolower($field->format) }} text"{{ ($field->pivot->required=='1') ? ' required' : '' }}>
@else
<input type="text" value="{{ strtoupper(trans('admin/custom_fields/general.encrypted')) }}" class="form-control disabled" disabled>
@endif
@@ -66,13 +66,13 @@
<p class="help-block">{{ $field->help_text }}</p>
@endif
<?php
$errormessage=$errors->first($field->db_column_name());
if ($errormessage) {
$errormessage=preg_replace('/ snipeit /', '', $errormessage);
print('<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> '.$errormessage.'</span>');
}
?>
<?php
$errormessage = $errors->first($field->db_column_name());
if ($errormessage) {
$errormessage = preg_replace('/ snipeit /', '', $errormessage);
print('<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> '.$errormessage.'</span>');
}
?>
</div>
@if ($field->field_encrypted)

View File

@@ -36,7 +36,7 @@
<!-- Custom Fieldset -->
<!-- If $item->id is null we are cloning the model and we need the $model_id variable -->
@livewire('custom-field-set-default-values-for-model',["model_id" => $item->id ?? $model_id ?? null ])
@livewire('custom-field-set-default-values-for-model', ["model_id" => $item->id ?? $model_id ?? null])
@include ('partials.forms.edit.notes')
@include ('partials.forms.edit.requestable', ['requestable_text' => trans('admin/models/general.requestable')])

View File

@@ -9,12 +9,15 @@
## {{ $assets->count() }} {{ trans('general.assets') }}
<table width="100%">
<tr><th align="left">{{ trans('mail.name') }} </th><th align="left">{{ trans('mail.asset_tag') }}</th><th align="left">{{ trans('admin/hardware/table.serial') }}</th> <th></th> </tr>
<tr><th align="left">{{ trans('mail.name') }} </th><th align="left">{{ trans('mail.asset_tag') }}</th><th align="left">{{ trans('admin/hardware/table.serial') }}</th><th align="left">{{ trans('general.category') }}</th> <th></th> </tr>
@foreach($assets as $asset)
<tr>
<td>{{ $asset->present()->name }}</td>
<td> {{ $asset->asset_tag }} </td>
<td> {{ $asset->serial }} </td>
<td> {{ $asset->model->category->name }}</td>
@if (($snipeSettings->show_images_in_email =='1') && $asset->getImageUrl())
<td>
<img src="{{ asset($asset->getImageUrl()) }}" alt="Asset" style="max-width: 64px;">

View File

@@ -1,15 +1,18 @@
@can('view', \App\Models\User::class)
<div id="userBulkEditToolbar">
{{ Form::open([
<div id="userBulkEditToolbar" class="pull-left" style="min-width:500px !important; padding-top: 10px;">
@if (request('status')!='deleted')
{{ Form::open([
'method' => 'POST',
'route' => ['users/bulkedit'],
'class' => 'form-inline',
'id' => 'usersBulkForm']) }}
@if (request('status')!='deleted')
<div id="users-toolbar">
<div id="users-toolbar" style="width:100% !important;">
<label for="bulk_actions" class="sr-only">{{ trans('general.bulk_actions') }}</label>
<select name="bulk_actions" class="form-control select2" style="min-width:300px;" aria-label="bulk_actions">
<select name="bulk_actions" class="form-control select2" style="width: 50% !important;" aria-label="bulk_actions">
@can('update', \App\Models\User::class)
<option value="edit">{{ trans('general.bulk_edit') }}</option>
@@ -25,7 +28,8 @@
</select>
<button class="btn btn-primary" id="bulkUserEditButton" disabled>{{ trans('button.go') }}</button>
</div>
{{ Form::close() }}
@endif
{{ Form::close() }}
</div>
@endcan

View File

@@ -215,6 +215,23 @@
</div>
</div>
<!-- Require Notes on checkin/checkout checkbox -->
<div class="form-group">
<div class="col-md-3">
<label>
{{ trans('admin/settings/general.require_checkinout_notes') }}
</label>
</div>
<div class="col-md-8">
<label class="form-control">
<input type="checkbox" value="1" name="require_checkinout_notes" {{ (old('require_checkinout_notes', $setting->require_checkinout_notes)) == '1' ? ' checked="checked"' : '' }} aria-label="require_checkinout_notes">
{{ trans('general.yes') }}
</label>
<p class="help-block">{{ trans('admin/settings/general.require_checkinout_notes_help_text') }}</p>
</div>
</div>
<!-- /.form-group -->
<!-- login text -->
<div class="form-group {{ $errors->has('login_note') ? 'error' : '' }}">

View File

@@ -309,10 +309,10 @@
<div class="row">
<!-- name -->
<div class="col-md-3 col-sm-2">
<div class="col-md-3">
{{ trans('admin/users/table.name') }}
</div>
<div class="col-md-9 col-sm-2">
<div class="col-md-9">
{{ $user->present()->fullName() }}
</div>
@@ -751,7 +751,7 @@
{{Helper::formatCurrencyOutput($user->getUserTotalCost()->total_user_cost)}}
<a id="optional_info" class="text-primary">
<x-icon type="caret-right" id="optional_info_icon" /></i>
<x-icon type="caret-right" id="optional_info_icon" />
<strong>{{ trans('admin/hardware/form.optional_infos') }}</strong>
</a>
</div>
@@ -1064,7 +1064,7 @@
<div class="tab-pane" id="managed-users">
@include('partials.locations-bulk-actions')
@include('partials.users-bulk-actions')
<table
@@ -1074,7 +1074,7 @@
data-pagination="true"
data-id-table="managedUsersTable"
data-toolbar="#usersBulkEditToolbar"
data-bulk-button-id="#bulkUsersEditButton"
data-bulk-button-id="#bulkUserEditButton"
data-bulk-form-id="#usersBulkForm"
data-search="true"
data-show-footer="true"

View File

@@ -0,0 +1,53 @@
<?php
namespace Tests\Feature\Console;
use App\Mail\UnacceptedAssetReminderMail;
use App\Models\CheckoutAcceptance;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class SendAcceptanceReminderTest extends TestCase
{
public function testAcceptanceReminderCommand()
{
Mail::fake();
$userA = User::factory()->create(['email' => 'userA@test.com']);
$userB = User::factory()->create(['email' => 'userB@test.com']);
CheckoutAcceptance::factory()->pending()->count(2)->create([
'assigned_to_id' => $userA->id,
]);
CheckoutAcceptance::factory()->pending()->create([
'assigned_to_id' => $userB->id,
]);
$this->artisan('snipeit:acceptance-reminder')->assertExitCode(0);
Mail::assertSent(UnacceptedAssetReminderMail::class, function ($mail) {
return $mail->hasTo('userA@test.com');
});
Mail::assertSent(UnacceptedAssetReminderMail::class, function ($mail) {
return $mail->hasTo('userB@test.com');
});
Mail::assertSent(UnacceptedAssetReminderMail::class,2);
}
public function testAcceptanceReminderCommandHandlesUserWithoutEmail()
{
Mail::fake();
$userA = User::factory()->create(['email' => '']);
CheckoutAcceptance::factory()->pending()->create([
'assigned_to_id' => $userA->id,
]);
$this->artisan('snipeit:acceptance-reminder')
->expectsOutput($userA->present()->fullName().' has no email address.')
->assertExitCode(0);
Mail::assertNotSent(UnacceptedAssetReminderMail::class);
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Tests\Feature\Users\Api;
use App\Models\Company;
use App\Models\Department;
use App\Models\Group;
use App\Models\Location;
use App\Models\User;
use Tests\TestCase;
class StoreUserTest extends TestCase {
public function testRequiresPermission()
{
$request = $this->actingAsForApi(User::factory()->create())
->postJson(route('api.users.store'))
->assertForbidden()
->json();
}
public function testCanSaveUserViaPost()
{
$admin = User::factory()->superuser()->create();
$manager = User::factory()->create();
$company = Company::factory()->create();
$department = Department::factory()->create();
$location = Location::factory()->create();
$group = Group::factory()->create();
$response = $this->actingAsForApi($admin)
->postJson(route('api.users.store'), [
'first_name' => 'Mabel',
'last_name' => 'Mora',
'username' => 'mabel',
'password' => 'super-secret',
'password_confirmation' => 'super-secret',
'email' => 'mabel@example.com',
'permissions' => '{"a.new.permission":"1"}',
'activated' => true,
'phone' => '619-555-5555',
'jobtitle' => 'Host',
'manager_id' => $manager->id,
'employee_num' => '1111',
'notes' => 'Pretty good artist',
'company_id' => $company->id,
'department_id' => $department->id,
'location_id' => $location->id,
'remote' => true,
'groups' => $group->id,
'vip' => true,
'start_date' => '2021-08-01',
'end_date' => '2025-12-31',
])
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('success');
$user = User::find($response['payload']['id']);
$this->assertEquals($admin->id, $user->created_by, 'Created by was not saved');
}
public function testDoesNotAcceptBogusGroupData()
{
$admin = User::factory()->superuser()->create();
$this->actingAsForApi($admin)
->postJson(route('api.users.store'), [
'first_name' => 'Mabel',
'username' => 'mabel',
'password' => 'super-secret',
'password_confirmation' => 'super-secret',
'groups' => ['blah'],
])
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->assertJson(
[
'messages' => [
'groups' =>
[0 => trans('The selected groups is invalid.')]
]
])
->json();
}
}