Compare commits

..

1361 Commits

Author SHA1 Message Date
snipe
07e252d58f This runs, but is broken
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 13:36:18 +01:00
snipe
5143b87b3a Revert "Ignore build assets"
This reverts commit a1c85fef14.

Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:08:37 +01:00
snipe
b1ef176dbc ¯\_(ツ)_/¯
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:08:05 +01:00
snipe
4226642b1b ¯\_(ツ)_/¯
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:07:57 +01:00
snipe
e40ba0f79c Set the URLs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 11:07:48 +01:00
snipe
b8c0279105 Revert "Ignore build assets"
This reverts commit a1c85fef14.
2025-07-28 10:51:16 +01:00
snipe
aae215a968 Updated package.lock
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 10:18:50 +01:00
snipe
bb847b9d99 Renamed vite.config.js to vite.config.jsx
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 10:18:42 +01:00
Shift
a1c85fef14 Ignore build assets 2025-07-28 08:55:52 +00:00
Shift
c65335a61e Add .vue extension to Vue imports 2025-07-28 08:55:52 +00:00
Shift
f95b014d2b Use ES modules 2025-07-28 08:55:51 +00:00
Shift
91e4fddd83 Remove webpack.mix.js 2025-07-28 08:55:51 +00:00
Shift
2af991023d Configure Vite 2025-07-28 08:55:50 +00:00
Shift
a993ab97c6 Update build scripts 2025-07-28 08:55:50 +00:00
Shift
f002c65608 Add npm dependencies 2025-07-28 08:55:50 +00:00
snipe
a7df6fb465 Added DB_SOCKET to example env
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 09:11:00 +01:00
snipe
133e7598e0 Merge pull request #17478 from grokability/library-upgrades
Library upgrades
2025-07-28 09:00:02 +01:00
snipe
c1a52ffa75 Bumped jspdf-autotable from ^3.8.4 to ^5.0.2
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:52:52 +01:00
snipe
4f46313388 Bumped tableexport to ^1.33.0
https://www.npmjs.com/package/tableexport.jquery.plugin
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:50:47 +01:00
snipe
03b2cc9cd2 Dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:43:22 +01:00
snipe
1a2bf8dc95 Bumped boostrap table from 1.24.1 to 1.24.2
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:43:17 +01:00
snipe
dd63fbeb84 Moved webpack to dev dependencies
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:38:26 +01:00
snipe
59e435c418 Bumped additional libraries
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:36:13 +01:00
snipe
f89f0a19b5 Updated axios
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:33:07 +01:00
snipe
cbc6ef95cb Removed babel-preset
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:26:52 +01:00
snipe
0ceecc9e1d Removed jquery UI
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:24:16 +01:00
snipe
c816902025 Updated postcss
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:19:56 +01:00
snipe
cfb03cdca0 Updated imagemin JS
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:07:34 +01:00
snipe
266f77b08c Update svg-sanitize
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 08:02:54 +01:00
snipe
257d58c236 Moved privacy policy link in settings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-28 03:30:10 +01:00
snipe
015f3d936c Merge pull request #17459 from grokability/#17441-add-status-to-id
Fixed #17441 - hardware listings "remembered" page numbers between statuses
2025-07-24 15:54:33 +01:00
snipe
18d2a0ffd7 Fixed #17441 - added status to table IDs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-24 15:47:26 +01:00
snipe
24afde0e46 Updated hash and minor version
Signed-off-by: snipe <snipe@snipe.net>
2025-07-24 15:35:33 +01:00
snipe
8499faa55a Fixed #17458 - use item_id instead of target_id for user history
Signed-off-by: snipe <snipe@snipe.net>
2025-07-24 15:29:36 +01:00
snipe
c60dd809b8 Removed debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-24 13:06:57 +01:00
snipe
297b8e33f2 Merge pull request #17436 from Godmartinz/fix-acceptance-markdown
Fixed #17394 - Changes the acceptance letter salutation to target
2025-07-23 22:55:05 +01:00
Godfrey M
b670b2014c accidentally removed a line 2025-07-23 09:56:19 -07:00
Godfrey M
440e969f52 remove unnecessary spacing 2025-07-23 09:47:03 -07:00
snipe
14b79f2f1c Fixed typo in id name
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 17:00:09 +01:00
snipe
00cf49a61f Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 16:10:47 +01:00
snipe
4f534e0e84 Bumped version/hash
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 16:02:51 +01:00
snipe
83a19fbbbf Merge pull request #17454 from uberbrady/de_flake_action_log_tests
Enforce order by ID for actionlog tests
2025-07-23 15:06:09 +01:00
snipe
610cb884fc Merge pull request #17452 from uberbrady/de_flake_tls_cert_file_test
This test was flaky, probably due to the PHP statcache.
2025-07-23 15:00:59 +01:00
snipe
ba92cec62b Merge pull request #17453 from grokability/#17316-checkbox-format-on-checkin-checkout
Fixed #17316 - handle checkboxes correctly in checkin/checkout
2025-07-23 14:56:24 +01:00
Brady Wetherington
d92e961a52 enforce order by ID for actionlog tests 2025-07-23 14:55:42 +01:00
snipe
b13e74756a Fixed #17316 - handle checkboxes correctly in checkin/checkout
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 14:51:34 +01:00
Brady Wetherington
4ef3072766 This test was flaky, probably due to the PHP statcache. 2025-07-23 14:15:52 +01:00
snipe
e96e2461d3 Merge pull request #17450 from grokability/copy-decrypted-custom-fields-to-clipboard
Fixed #17447 - decrypt before copying to clipboard
2025-07-23 12:41:02 +01:00
snipe
7a2e2be169 Fixed #17447 - decrypt before copying to clipboard
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 12:39:54 +01:00
snipe
8d2a5a7e4a Added location and defaultLoc to searchable relations in audit log
Signed-off-by: snipe <snipe@snipe.net>
2025-07-23 12:28:23 +01:00
snipe
b7b0e4fab5 Merge pull request #17447 from Godmartinz/make-custom-fields-copyable
Adds #17133 Copy ability to all Custom fields
2025-07-23 12:11:47 +01:00
Godfrey M
a624a79b30 add terenary 2025-07-22 16:36:19 -07:00
Godfrey M
313135da6f Merge branch 'develop' into make-custom-fields-copyable 2025-07-22 16:26:57 -07:00
Godfrey M
58d27d1247 move copy button to front 2025-07-22 16:17:52 -07:00
snipe
edfb28168f Merge pull request #17446 from marcusmoore/snipe-it-17445-move-jobtitle-under-assigned_to-in-assettransformer
Fixed #17445 - move jobtitle under assigned_to in AssetTransformer
2025-07-22 20:27:01 +01:00
Godfrey M
8d0e03bb06 fix copy target 2025-07-22 11:57:46 -07:00
Marcus Moore
855f6f77cf Re-add sorting 2025-07-22 11:49:32 -07:00
Godfrey M
6236cffe14 adds copy links for filled custom fields 2025-07-22 11:49:11 -07:00
Marcus Moore
322a71fbb8 Add jobtitleFormatter 2025-07-22 11:37:34 -07:00
Marcus Moore
4d9f8476f3 Update field key in AssetPresenter 2025-07-22 11:07:58 -07:00
Marcus Moore
d7d93b14b2 Move jobtitle under assigned_to 2025-07-22 11:02:26 -07:00
snipe
d1af3ece6e One more tweak to login checkbox
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 15:25:09 +01:00
snipe
8153b20984 Check for demo mode on UI for able to login
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 15:18:34 +01:00
snipe
a50f605c29 Merge pull request #17443 from grokability/added-not-allowed-cursor
Adds disabled cursor on uneditable fields in user create/edit
2025-07-22 15:13:14 +01:00
snipe
daf23edd10 Adds disabled cursor on uneditable fields in user create/edit
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 15:10:27 +01:00
snipe
2eaaeb8259 Merge pull request #17423 from grokability/tighter-permissions-on-non-admins
Tighter permissions on non-admins and demo modes
2025-07-22 14:32:50 +01:00
snipe
a02c62d62c Fixed tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 14:12:51 +01:00
snipe
e0232a8e84 Renamed gate
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 14:02:18 +01:00
snipe
6ea5693b2f Updated comment, removed log error statement
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 13:59:58 +01:00
snipe
030c2114d1 Merge pull request #17442 from grokability/user-api-eula-fix
Fixed FD-49886 - Optimize user queries
2025-07-22 13:39:36 +01:00
snipe
2cb18e3668 Remove fields from query - eulas was querying actionlogs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 13:25:41 +01:00
snipe
cd9f8be563 Optimize for when we already have the counts
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 13:25:16 +01:00
snipe
a02792e9bf Merge pull request #17300 from uberbrady/add_actionlog_tests
Fixed #17071 - Adding various tests of the contents of ActionLogs for lots of events
2025-07-22 10:51:30 +01:00
snipe
41bb422244 Merge pull request #17439 from marcusmoore/component-file-test-fix
Attempt to fix flaky file upload tests pt2
2025-07-21 23:16:03 +01:00
Marcus Moore
54663d3342 Pass order to api in test 2025-07-21 15:10:35 -07:00
snipe
2529f7369f Merge pull request #17438 from grokability/file-upload-tests-fix
Attempt to fix flaky file upload tests
2025-07-21 22:48:38 +01:00
snipe
909c33dccf Fixed order location
Signed-off-by: snipe <snipe@snipe.net>
2025-07-21 22:45:17 +01:00
snipe
1adc9f1aa9 Attempt to fix flaky tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-21 22:18:15 +01:00
Godfrey M
49da9e58fd changed markdown to point to assignedto name 2025-07-21 12:00:00 -07:00
snipe
f3e288d078 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-21 17:46:49 +01:00
snipe
988000952e Fixed RB-3997
Signed-off-by: snipe <snipe@snipe.net>
2025-07-21 13:48:01 +01:00
snipe
6537f3794b Merge pull request #17292 from Godmartinz/fail_with_inputs
FIXED: #17194 Return to bulk edit with errors and inputs
2025-07-21 12:03:52 +01:00
snipe
d31718ba8a Merge pull request #17389 from grokability/use-transformer-for-api-asset-model-response
Use standard model transformer for asset model API response
2025-07-21 11:52:25 +01:00
snipe
9dd4bc5fa8 Merge pull request #17391 from Godmartinz/add-components-notifications
FIXED: #13844 Adds Webhook and Mail Notifications for Components
2025-07-21 11:51:30 +01:00
snipe
df5f1bd522 Merge pull request #17434 from grokability/dependabot/github_actions/develop/codacy/codacy-analysis-cli-action-4.4.7
Bump codacy/codacy-analysis-cli-action from 4.4.5 to 4.4.7
2025-07-21 11:45:04 +01:00
dependabot[bot]
ddffab9169 Bump codacy/codacy-analysis-cli-action from 4.4.5 to 4.4.7
Bumps [codacy/codacy-analysis-cli-action](https://github.com/codacy/codacy-analysis-cli-action) from 4.4.5 to 4.4.7.
- [Release notes](https://github.com/codacy/codacy-analysis-cli-action/releases)
- [Commits](https://github.com/codacy/codacy-analysis-cli-action/compare/v4.4.5...v4.4.7)

---
updated-dependencies:
- dependency-name: codacy/codacy-analysis-cli-action
  dependency-version: 4.4.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 09:26:25 +00:00
snipe
0c34073582 Namespace fix for presenter
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 17:17:04 +01:00
snipe
14674947cb Fixed test namespace
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 17:15:51 +01:00
snipe
51bccdbd66 Merge pull request #17424 from marcusmoore/chore/livewire-ugprade
Bumped livewire to v3.6.4
2025-07-18 17:12:14 +01:00
snipe
f0fbb3cf36 Uncomment permissions test
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:31:31 +01:00
Brady Wetherington
0cc47aacbe Got tests to pass by making them match our current reality, rather than wishes 2025-07-18 16:14:32 +01:00
snipe
fafd592290 Wrap groups and activated into the other canEditAuthFields gate
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:03:43 +01:00
snipe
40e754b8c3 Additional criteria for the canEditAuthFields gate
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:03:22 +01:00
snipe
483301db7a Changed some of the gating logic for demo mode. Sigh.
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:02:59 +01:00
snipe
218606fbd6 Updated view permissions
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:02:41 +01:00
snipe
c601b8e62c Updated test
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 16:02:11 +01:00
snipe
2bd68ec991 Uncommented importer gate
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 13:17:25 +01:00
snipe
66842648ed Removed debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 13:17:10 +01:00
snipe
ce54b9a7b5 Removed duplicate alert
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 13:16:59 +01:00
Brady Wetherington
8a5f6d2a5d Refactor base test into Trait, clean test output for easier comparison 2025-07-18 13:16:35 +01:00
snipe
1d86a5476f Updated language
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 12:45:43 +01:00
snipe
ca4d3f6bce Changed gate name, removed debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-18 12:45:32 +01:00
Godfrey M
2812f2ce92 remove log 2025-07-17 15:04:42 -07:00
Godfrey M
5c623db798 fix redirect 2025-07-17 14:57:00 -07:00
Marcus Moore
edaf005fe1 Bump livewire to v3.6.4 2025-07-17 14:15:10 -07:00
snipe
4f6e407247 More consistent language degarding the demo
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 21:13:13 +01:00
snipe
e30881239c A few more clean ups for demo mode
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 21:08:50 +01:00
snipe
bbde2cc4b2 Use history blade component
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 21:04:11 +01:00
snipe
16d18c79d7 Fixed email editable field
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 21:03:20 +01:00
snipe
a0d2cb8a03 Clearer (if longer) gate name
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:47:20 +01:00
snipe
1bb5dc7e69 Added one more test
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:40:01 +01:00
Brady Wetherington
58759acfe4 Think I hit _all_ of the tests we need to mess with here 2025-07-17 20:15:01 +01:00
snipe
0cd5136052 Added translations
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:12:52 +01:00
snipe
b3c6fe5369 Use both new gates in user edit
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:12:46 +01:00
snipe
599718f84e Use new gates in controllers
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:12:32 +01:00
snipe
d9a5452388 Defined new gates
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:12:10 +01:00
snipe
0fe49e04bf Attempt to use a gate here?
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:09:27 +01:00
snipe
a98d3fb4dc Check for the format of the permissions (string, object, array)
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:09:17 +01:00
Godfrey M
8c670d1832 clean up 2025-07-17 12:08:49 -07:00
snipe
c232f490bc Show user log
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:08:40 +01:00
snipe
c7280953dd Added/updated tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 20:08:32 +01:00
Godfrey M
8f4c606c64 remove var dumps 2025-07-17 12:04:33 -07:00
Godfrey M
6740afab42 radio buttons values return correctly 2025-07-17 11:59:09 -07:00
Godfrey M
5df22b3e6a checkboxes properly check 2025-07-17 11:56:52 -07:00
snipe
3d9d18a0d5 Fixed weird CSS quirk
Signed-off-by: snipe <snipe@snipe.net>
2025-07-17 19:22:23 +01:00
Godfrey M
fc469707a3 clean up 2025-07-16 10:51:33 -07:00
snipe
77fdc370c7 Merge pull request #17415 from uberbrady/clean_unaccepted_assets_report
[FD-47386, FD-49095] New Artisan command to clean checkout acceptances
2025-07-16 17:34:49 +01:00
snipe
301290fb6d Send emails on acceptance even if signature is not required
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 17:02:04 +01:00
snipe
07fffe2f79 Merge pull request #17410 from grokability/remove-password-from-welcome
Remove password from welcome email, prompt for reset instead
2025-07-16 16:54:07 +01:00
snipe
0227a63fa5 Slightly clearer language
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:31:45 +01:00
snipe
27764b863c Updated language
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:25:36 +01:00
snipe
032fd75f9e Added default invite password token timeout
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:23:51 +01:00
snipe
0bf4f861f3 Nicer debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:23:25 +01:00
snipe
fd8f90cb52 Added new password broker for longer toekn lifetime
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:23:11 +01:00
snipe
b6c6b025c8 Added expiry language
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:20:26 +01:00
snipe
3d89e98d1f Small tweaks to welcome email blade
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 16:20:15 +01:00
Brady Wetherington
7c5110ed5d Add more action logs tests everywhere I can think of it. 2025-07-16 16:20:06 +01:00
Brady Wetherington
0a474f48ad WIP: Adding various tests of the contents of ActionLogs for lots of events 2025-07-16 16:20:06 +01:00
Brady Wetherington
c409bfd5be New Artisan command to clean checkout acceptances and a migration that runs it 2025-07-16 16:06:23 +01:00
snipe
39d5d5b2e0 Merge branch 'develop' into remove-password-from-welcome 2025-07-16 15:05:13 +01:00
snipe
8a80d9009d Refomatted hidden array
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 12:24:48 +01:00
Godfrey M
f62b5df566 use ternaries instead of optionals 2025-07-15 15:40:21 -07:00
Godfrey M
214757ab0b fix mailable 2025-07-15 12:04:36 -07:00
Godfrey M
f130186b37 add Component Checkin Mail 2025-07-15 11:56:34 -07:00
Godfrey M
2244eebc3b add Component Checkout Mail 2025-07-15 11:00:39 -07:00
snipe
4176792f2d Translate field
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 16:32:17 +01:00
snipe
1e6cef52c9 Fixed tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 15:17:08 +01:00
snipe
a0f4f30a50 Added try/catch
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 15:13:33 +01:00
snipe
4cbf6ac393 Re-add /setup crential email
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:20:13 +01:00
snipe
af7425d8e6 Remove unused variable
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:19:12 +01:00
snipe
3fea909d3f Removed send credentials option from user controller
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:14:10 +01:00
snipe
7c37d40677 Use plaintext in the database so that the password will never be valid
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:13:50 +01:00
snipe
3a97c27350 Removed logging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:13:29 +01:00
snipe
e0516a52a8 Formatting change
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:12:55 +01:00
snipe
a85ec6efb3 Set token in welcome email constructor
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:12:42 +01:00
snipe
3795c74814 Added string
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:12:26 +01:00
snipe
27954dc6d3 Use password reset token
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:12:18 +01:00
snipe
68c4187a09 Removed email creds option from user create
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:11:15 +01:00
snipe
b9834231f3 Remove email credentials chexkbox
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 14:08:36 +01:00
snipe
2be343ea1c More specific no password
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 13:11:45 +01:00
snipe
109fe1b62c Use no password as temp password
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 13:11:18 +01:00
snipe
63d691a63c Removed noisy log
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 13:10:48 +01:00
snipe
6f57d6b876 Merge pull request #17407 from grokability/fixes-signature-pad-chrome
Fixed display of acceptance button if signature is not required
2025-07-15 10:58:34 +01:00
snipe
e0bad99ea1 Fixes display of acceptannce button if signature is not required
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 10:55:30 +01:00
snipe
e39eb09cfb Merge pull request #17390 from Godmartinz/unhandled-redirect-error
FIXED redirect option being NULL
2025-07-10 19:41:40 +01:00
Godfrey M
64d397c3f3 add component notification tests 2025-07-10 11:26:10 -07:00
Godfrey M
465ac1d1e1 remove ternary 2025-07-10 08:39:13 -07:00
Godfrey M
18d6becebc populate other_redirect in store method 2025-07-10 08:36:15 -07:00
snipe
943a4093ad Use standard model transformer for asset model API response
Signed-off-by: snipe <snipe@snipe.net>
2025-07-10 15:42:39 +01:00
snipe
b0917a5131 Merge pull request #17385 from grokability/17383-fix-api-route-path
Fixed #17383 - re-add `/hardware/` as an object type in the file upload API
2025-07-10 13:10:43 +01:00
snipe
0972c4e340 Re-added /hardware/ as viable route for API file uploads
Signed-off-by: snipe <snipe@snipe.net>
2025-07-10 13:06:22 +01:00
Godfrey M
3bbd0fdbcd google notifications fires properly 2025-07-09 17:02:51 -07:00
snipe
43a237bf95 Merge pull request #17378 from grokability/phpcs/models
Code formatting fixes
2025-07-09 21:55:30 +01:00
snipe
95f867b267 Code formatting fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 21:48:53 +01:00
snipe
e96daf469a Better phrasing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 21:00:54 +01:00
snipe
f2cdfe9e47 Normalize textarea for notes in acceptance form
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:58:07 +01:00
snipe
929b67e768 Merge pull request #17376 from grokability/small-tweak-to-acceptance-ui
Better indicate via submit button colors and messaging that something is about to be accepted or declined
2025-07-09 20:21:50 +01:00
snipe
0573dc136a Put the sig check back
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:16:30 +01:00
snipe
48588f6a9e Small UI sugar on the acceptance/signature screen
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:08:19 +01:00
Godfrey M
8214b11da5 MS teams fires properly 2025-07-09 11:44:53 -07:00
Godfrey M
36090bf83e checked in notification fires, updated icon translation usage 2025-07-09 11:35:24 -07:00
Godfrey M
bffb2fe82f checkout notification fires 2025-07-09 11:23:27 -07:00
Godfrey M
500cbf5d92 add component checkout notification, update checkout blade, update listener 2025-07-09 11:12:18 -07:00
snipe
88579b9bf3 Merge pull request #17374 from uberbrady/improve_inline_videos
Fixed [FD-49538 ] - use a Video tag for video files for non-Safari usage
2025-07-09 15:47:19 +01:00
snipe
e8bb9bde99 Fixed #8201 - splits first_name and last_name in user export
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 15:46:42 +01:00
Brady Wetherington
0ee3cca4da Use a Video tag for video files for non-Safari usage 2025-07-09 15:15:53 +01:00
snipe
f89ee6b7f2 Merge pull request #17361 from Godmartinz/return-custom-textarea_input
Fixed #7957 - custom field Textarea input not retaining when switching Asset Models with shared fields
2025-07-08 22:02:07 +01:00
snipe
aebfb52c85 Merge pull request #17362 from Godmartinz/license-redirect-bug
Fixed #17310 - 500 on redirect when checking in a license seat
2025-07-08 21:59:22 +01:00
Godfrey M
667bd7af0e fix checkout_to_type being null when checking in 2025-07-08 13:53:10 -07:00
snipe
bbf69bc582 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-07-08 21:26:15 +01:00
snipe
f2b7a3d002 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-08 21:25:31 +01:00
Godfrey M
3fd9e3ab56 include textareas input return 2025-07-08 12:02:56 -07:00
snipe
8e11466a54 Added query scope
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 22:09:23 +01:00
snipe
dade9797d5 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 21:46:13 +01:00
snipe
97c1e65ffc Fixed fieldname
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 20:58:26 +01:00
snipe
b4e22f4a21 Small fix for seat listing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 20:56:26 +01:00
snipe
58b6feb3ca Merge pull request #17356 from grokability/show-only-taken-licenses
[FD-49569 ] - Show only assigned in license tab
2025-07-07 17:05:38 +01:00
snipe
41c4920d45 Show only assigned in license tab
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 16:58:36 +01:00
snipe
d1ddd8de98 Re-add column selector
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 16:23:48 +01:00
snipe
f41307eb4a Merge pull request #17353 from grokability/fixes-#14295-send-acceptance-on-signing
Fixed #14295 - allow user to receive an email PDF upon signing
2025-07-07 15:59:03 +01:00
snipe
59de77feb0 Use company name if provided instead of site name
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:55:40 +01:00
snipe
8ebbcf6e80 Removed console commands
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:55:31 +01:00
snipe
24c6e836dd Added checkbox toggle
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:51:24 +01:00
snipe
8e38b3898e Fixes #14295 - allow user to receive an email PDF upon signing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:38:36 +01:00
snipe
ce9a5e35c9 Merge pull request #17352 from grokability/fixed-#17273-notes-in-upcoming-audit
Fixed #17273 -  switch to HTML table from markdown
2025-07-07 14:42:43 +01:00
snipe
b092779697 Fixed #17273 - switch to HTML table from markdown
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 14:37:38 +01:00
snipe
ab30a96d16 Merge pull request #17327 from Godmartinz/asset_model_redirect
FIXED: #15861 adds a redirect option for asset model and previous page
2025-07-07 14:00:31 +01:00
snipe
dab0fb16ad Merge pull request #17291 from Godmartinz/fix_bulk_checkout_focus
Fixes #12094 - Adds focus to select2 in bulk checkout
2025-07-07 13:57:12 +01:00
snipe
5be398bc99 Merge pull request #17350 from grokability/tighter-control-on-company
Fixes #17302 - Tighter control on company
2025-07-07 13:45:08 +01:00
snipe
fe4172957f Merge pull request #17351 from grokability/smaller-pdfs
Fixed #17349 - enable_font_subsetting in PDFs
2025-07-07 13:33:29 +01:00
snipe
ff3a59d347 Fixed #17349 - enable_font_subsetting in PDFs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:32:25 +01:00
snipe
f9aedea26f Eager load admin
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:21:07 +01:00
snipe
5abd2c7151 Added tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:12:16 +01:00
snipe
bfcaf4f37b Removed unecessary use statement
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:36:59 +01:00
snipe
5f4e1835bc Removed unused method
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:36:16 +01:00
snipe
c1f1ae6b64 Removed logging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:31:12 +01:00
snipe
c4fcc6c24e Removed direct scoping calls
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:26:30 +01:00
snipe
dd73ad9941 Merge pull request #17341 from Godmartinz/query_error_rb19824
Fixed #17193: perform Orderby before Collection in Bulk Assets Controller
2025-07-07 11:19:37 +01:00
snipe
ac21f7569f Merge pull request #17346 from grokability/add-video-uploads
[FD-49538] Add video/audio uploads
2025-07-07 11:15:19 +01:00
snipe
4ef0158da4 Use preview instead of image text
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:19:29 +01:00
snipe
4db3b3ba0e Use config array for extensions in restore tool
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:19:13 +01:00
snipe
dc43d85323 Check for audio files
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:15:30 +01:00
snipe
62651f381c Expand safe allowed inline files
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:13:57 +01:00
snipe
3e9098907a Use config file for file types/mimes
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:11:31 +01:00
snipe
e18df250f8 Removed console debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:08:08 +01:00
Godfrey M
be5c5a51da get desired behavior of select-2 2025-07-02 11:20:04 -07:00
Godfrey M
a728fad675 perform orderBy on query before converting to a Collection 2025-07-02 11:06:35 -07:00
snipe
185629b310 Merge pull request #17338 from grokability/small-depreciation-tweaks
Fixed #1909 - Small depreciation tweaks
2025-07-02 18:37:21 +01:00
snipe
30ebea4f2d Return int count
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 18:24:27 +01:00
snipe
b135c1eac2 Updated language strings in view
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 18:15:06 +01:00
snipe
88fef73d6f Use new translation
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:54:10 +01:00
snipe
556a9039e9 New strings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:55 +01:00
snipe
cdfe6c21c1 Tightened HTML, added translations
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:42 +01:00
snipe
b094ebdd66 Removed validation of > 0
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:32 +01:00
snipe
526a7ddea6 Merge pull request #17337 from grokability/fixes-#17112-ldap-location-set-to-0
Fixed #17112 - Set location ID to null instead of 0
2025-07-02 17:20:34 +01:00
snipe
bb5ad31cba Merge pull request #17336 from uberbrady/safer_deserialize
Use safer deserialization defaults
2025-07-02 17:15:48 +01:00
snipe
549da2efed Set location ID to null instead of 0
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:15:18 +01:00
snipe
e5e586dc43 Attempt to generalize companyable in company scope
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:12:55 +01:00
Brady Wetherington
8a682beb0e Use safer deserialization defaults 2025-07-02 14:49:12 +01:00
snipe
699e9f75c9 Fixed RB-19892
Check for location before trying to grab company property

Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 11:57:42 +01:00
snipe
759e30977b Merge pull request #17333 from grokability/fixes-#17326-dash-sorting
Fixed #17326 - sorting on dashboard
2025-07-02 11:43:48 +01:00
snipe
6cfdb49cc3 Fixed #17326 - sorting on dashboard
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 11:41:21 +01:00
snipe
1195121bf0 Merge pull request #17330 from uberbrady/add_escaping_to_action_logs
Add escaping to user_agent and remote_ip variables for API results
2025-07-01 23:30:41 +01:00
Brady Wetherington
8bc067b18b Add escaping to user_agent and remote_ip variables for API results 2025-07-01 23:22:09 +01:00
Godfrey M
76f59f7b85 fix variables 2025-07-01 12:52:16 -07:00
Godfrey M
55ebb4671f update check in and check out controllers 2025-07-01 12:44:26 -07:00
Godfrey M
8a9cf07063 editing controllers and edit blades for other categories 2025-07-01 12:31:55 -07:00
Godfrey M
ca9ff8cf19 set return type for RedirectOptions 2025-07-01 12:04:02 -07:00
Godfrey M
7217d9c427 adds redirect to previous page, use match instead of switch cases 2025-07-01 11:55:46 -07:00
Godfrey M
9d712ad8f1 clean up code 2025-07-01 10:56:02 -07:00
Godfrey M
f3e49e7010 add asset model as a redirect option 2025-07-01 10:43:00 -07:00
snipe
ba94f1b920 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-06-30 11:45:43 +01:00
snipe
edcd46dd67 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-06-30 11:44:46 +01:00
snipe
5cf6c89dde Merge pull request #17322 from grokability/fixes-#8484-add-supplier-to-license-relationship
Fixed #8484 - added supplier to license relationship
2025-06-30 11:29:07 +01:00
snipe
58676b1f83 Fixed #8484 - added supplier to license relationship
Signed-off-by: snipe <snipe@snipe.net>
2025-06-30 11:27:30 +01:00
snipe
8ff7c30e5a Merge pull request #17315 from grokability/traitify-uploads
Added HasUploads trait and remove uploads method for models
2025-06-27 20:00:28 +01:00
snipe
cd989768d4 Added HasUploads trait and remove uploads method for models
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 19:32:22 +01:00
snipe
6cbdefe3d9 Small regressions
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 13:04:02 +01:00
snipe
c6ecc0d8e8 Merge pull request #17311 from grokability/rwork-bulk-api-to-smaller-pr
Fixed #9413 and rework upload API for bulk and better responses (refactor of #16964)
2025-06-27 12:43:34 +01:00
snipe
e0f5663bf4 Requested changes
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 12:37:11 +01:00
snipe
aafc8996c1 phpcbf fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 12:18:54 +01:00
snipe
128da40cbf Added comments
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 12:03:12 +01:00
snipe
ea0460e97e Remove unused API files controllers
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 11:51:36 +01:00
snipe
d8e7123576 Added uploaded files API controllers and presenters
Signed-off-by: snipe <snipe@snipe.net>
2025-06-27 11:37:31 +01:00
snipe
6f45ec655f Merge pull request #16857 from realchrisolin/generic_tape
Generic tape
2025-06-25 14:59:57 +01:00
snipe
ec17c168ea Merge pull request #17222 from grokability/fixes-#17221-move-table-featueres-into-js
Fixed #17221 - Moved common table elements to partial
2025-06-25 14:52:17 +01:00
snipe
119b097521 More cleanup
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:43:09 +01:00
snipe
c731633a84 Fixed weird formatting
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:40:26 +01:00
snipe
6a4d6ade39 Fixed typo
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:39:46 +01:00
snipe
b3b4697fc9 More whitespace cleanup
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:38:25 +01:00
snipe
72c706d697 Aaaand one more
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:30:34 +01:00
snipe
8a7af24bd4 More whitespace
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:29:33 +01:00
snipe
dd01bd3e5f Cleaned up whitespace
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:29:01 +01:00
snipe
59cade9f82 Cleaned up whitespace
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:28:20 +01:00
snipe
6bb9b79832 Tweaked dashboard
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:11:29 +01:00
snipe
f8fe7b5803 Removed whitespace
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:11:19 +01:00
snipe
4d6279d61c Added JS to handle data-dash attribute overrides
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:11:04 +01:00
snipe
5ef581f328 Fixed typo
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 14:10:51 +01:00
snipe
20a59c343e Merge pull request #17296 from uberbrady/improve_fmcs_locations_test
Fixed #17190 and [FD-49375]: Do FMCS testing 'async' to keep from blowing out the whole settings page
2025-06-25 13:10:54 +01:00
snipe
d6feb522b7 Merge pull request #17297 from grokability/fixes-#17259-#15239-external-avatar
Fixed #15239 and #17259 - better handle external avatars
2025-06-25 13:09:33 +01:00
snipe
951aee8292 Fixes #15239 and #17259 - better handle external avatars
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 13:03:51 +01:00
Brady Wetherington
6e2d7912b5 Do the FMCS testing 'async' to keep from blowing out the whole page 2025-06-25 12:59:37 +01:00
snipe
b20925b550 Fixed #17282 - removed erroneous update gate for user-license endpoint
Signed-off-by: snipe <snipe@snipe.net>
2025-06-25 11:03:06 +01:00
Godfrey M
c8b213c190 remove some changes, move error bag 2025-06-24 13:10:32 -07:00
Godfrey M
942de9dce5 got validation to redirect back to form and display 2025-06-24 12:42:07 -07:00
Godfrey M
483f684b04 adds focus to select 2 bulk checkout 2025-06-24 09:58:38 -07:00
snipe
26774b4193 Merge pull request #17287 from uberbrady/limit_license_seat_increments
Fixed [FD-45786] - Limit changing of asset seat count to no more than 10k at a time
2025-06-24 17:43:02 +01:00
Brady Wetherington
a7a597d609 Added some tests around license seat changes 2025-06-24 17:35:49 +01:00
snipe
1c12b9278a Merge pull request #17116 from marcusmoore/17065-send-alert-to-assigner-upon-response
Fixed #17065 - allow sending acceptance alert to initiator.
2025-06-24 17:30:47 +01:00
Brady Wetherington
de4764bd05 Limit changing of asset seat count to no more than 10k at a time 2025-06-24 13:53:11 +01:00
snipe
c1e7a78d23 Merge pull request #17284 from Godmartinz/fix-depreciation-choice-in-transformer
FIXED: #14869 changes the depreciation method selected for Assets index table
2025-06-23 20:49:25 +01:00
Godfrey M
8894bb91cc fix method choice in asset transformer for depreciations 2025-06-23 12:37:29 -07:00
snipe
c955126f01 Merge pull request #17283 from Godmartinz/multiclick_checkout_bug
Fixed #14077: Disables checkout button after submitting
2025-06-23 20:30:38 +01:00
Godfrey M
ce53b48d04 disable checkout button after submitting 2025-06-23 12:18:24 -07:00
snipe
6015aeddee Merge pull request #17209 from Godmartinz/saml_xml_update_bug
Fixed #17089: SAML metadata now updating with new XML uploads
2025-06-23 16:40:35 +01:00
snipe
7b04e30964 Merge pull request #17280 from grokability/fixes/#7246-manager-id-in-import
Fixed #7246 - added manager employee/username number to importer
2025-06-23 16:07:14 +01:00
snipe
6794f5e783 Added logging for manager import
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 15:46:04 +01:00
snipe
6e41ceff39 Fixed parameter order
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 15:41:56 +01:00
snipe
7ab47ff0de Fixed #7246 - added manager employee number to importer
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 15:11:57 +01:00
snipe
92d24d8702 Merge pull request #17277 from grokability/#17264-add-notes-to-bulk
Fixed #17264: add notes to bulk asset edit
2025-06-23 12:36:50 +01:00
snipe
bcbfd46682 Update controller
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 12:32:33 +01:00
snipe
bfd96a695f Add notes field and nulling checkbox
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 12:32:28 +01:00
snipe
f27e8534dc Make sure $item exists
Signed-off-by: snipe <snipe@snipe.net>
2025-06-23 12:32:15 +01:00
snipe
040cd7ddbf Updated string to use shorter version
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 20:56:37 +01:00
snipe
8d6b21a076 Merge pull request #17241 from spencerrlongg/bug/17239-possible-500-when-checking-in-license-assigned-to-a-soft-deleted-user
Fixed #17239 - Add `withTrashed` User Search On License Checkin
2025-06-22 20:14:03 +01:00
snipe
2d36b25017 Merge pull request #17258 from Robert-Azelis/patch-13
Company info - user print assets
2025-06-22 20:13:24 +01:00
snipe
1f9e4306ae Fixed #17261 - check for either old or new label engine QR
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 20:07:30 +01:00
snipe
5242e0b36e Merge pull request #17270 from grokability/fixes/#17249-order-by-location-in-maintenances
Fixed #17249 - sort by location in asset maintenances
2025-06-22 19:41:14 +01:00
snipe
e50505532e Fixes #17249 - sort by location in asset maintenances
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:38:39 +01:00
snipe
f05ef18d55 Merge pull request #17269 from grokability/fixes/#17256-user-eula-view
Fixed #17256: fixed permissions for non-super-admins to view their own EULAs
2025-06-22 19:29:41 +01:00
snipe
f6eccd7277 Added profile routes
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:25:30 +01:00
snipe
4d1258c64b Fixed downloadformatter
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:25:17 +01:00
snipe
103cbfd038 Use profile eula API view
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:25:05 +01:00
snipe
47069ad3f4 Added stored eula method on profile
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:24:45 +01:00
snipe
317f620992 Added profile controller
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:24:19 +01:00
snipe
8f43694582 Added eula download with user check in profile controller
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:24:06 +01:00
snipe
df30076ffd Update gate for user
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 19:23:50 +01:00
snipe
f81750617e Fixed mismatched html header tag
Signed-off-by: snipe <snipe@snipe.net>
2025-06-22 17:30:13 +01:00
Robert-Azelis
e4534c4319 Company info - user print assets 2025-06-20 10:33:09 +02:00
snipe
d6c09aae6b Updated paveit command to delete by user_id instead of ID
Signed-off-by: snipe <snipe@snipe.net>
2025-06-18 21:15:04 +01:00
spencerrlongg
d3d5230d0c add with trashed to user search 2025-06-17 19:15:13 -05:00
Marcus Moore
6f3c5c44a5 Remove assertion 2025-06-17 14:31:14 -07:00
Marcus Moore
67b32ca14d Mail content improvements 2025-06-17 14:22:54 -07:00
Marcus Moore
bffaf477ea Method order 2025-06-17 14:22:44 -07:00
Marcus Moore
cba45ece12 Add image 2025-06-17 13:57:40 -07:00
Marcus Moore
3290d7f401 Add translations 2025-06-17 13:27:05 -07:00
Marcus Moore
ef3827376d Add todo 2025-06-17 13:17:42 -07:00
Marcus Moore
4ae8a91051 Remove unused method 2025-06-17 12:34:43 -07:00
Marcus Moore
ff4819ac68 Get licenses working 2025-06-17 12:34:16 -07:00
Marcus Moore
58af133853 Re-add some test cases 2025-06-17 11:51:33 -07:00
Godfrey M
8199cd2118 comment merge methods for now 2025-06-17 10:59:57 -07:00
snipe
9f02b80cf1 Merge pull request #17233 from grokability/fixes-#17232-duplicate-expected-checkin-field
Fixed #17232 - removed duplicate expected_checkin field on asset edit/create
2025-06-17 18:14:36 +01:00
snipe
d3e4e81168 Fixed #17232 - removed duplicate expected_checkin field
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 18:08:21 +01:00
snipe
38195c0a8f Merge pull request #17225 from grokability/fixes-ui-issues-in-manager-view
Fixes #17227 and #17228 - UI issues in manager view
2025-06-17 17:28:52 +01:00
snipe
5fa11e4278 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 16:57:26 +01:00
snipe
c39b52fcb5 Move select list closer to the table, removed awkward icon
This makes the view more consistent with the normal layout and removes the CSRF that was being passed in the GET on the form.submit

Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 16:57:17 +01:00
snipe
ec65fc1e65 Move manager view option to misc section
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 16:56:56 +01:00
snipe
32d8646d96 Add @lukaskraic as a contributor 2025-06-17 16:26:17 +01:00
snipe
e8835fc2b1 Merge pull request #17096 from lukaskraic/feature/manager-view-v2
Manager View Feature
2025-06-17 16:24:40 +01:00
snipe
054a06c5dc Update resources/lang/en-US/admin/settings/general.php
Co-authored-by: Marcus Moore <contact@marcusmoore.io>
2025-06-17 15:39:26 +01:00
snipe
9c61d2eb22 Removed common elements in tables
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 14:25:02 +01:00
snipe
d66b6cfee6 Merge pull request #17220 from grokability/added-advanced-search-to-activity-log
Added advanced search to activity log
2025-06-17 13:24:31 +01:00
snipe
89c0427b2f Added fullscreen option
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:24:02 +01:00
snipe
3fec10d447 Added advanced search to activity log
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:20:39 +01:00
snipe
f8b4981bfe Merge pull request #17219 from grokability/fixes-#17213-search-by-admin
Fixes #17213:  search by admin (created_by) in activity report
2025-06-17 13:02:51 +01:00
snipe
130669a2f9 Added adminuser relationship in Searchable to concat first name and last name
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:01:57 +01:00
snipe
c2c79ee231 Make created_by searchable
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:56:39 +01:00
snipe
86f10bd702 Added employee number to searchable relation
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:56:30 +01:00
snipe
b496a06fc0 Merge pull request #17218 from grokability/fixed-#17212-hide-columns-on-print
Fixed #17212 - hide columns from print view
2025-06-17 12:37:43 +01:00
snipe
f865a6cb37 Added 'printIgnore' => true, to presenters to hide checkbox and action column from print view
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:35:05 +01:00
snipe
89186ea4f8 Merge pull request #17195 from grokability/#17192-fixes-wonky-layout-on-bulk-edit
Fixed #17192 - wonky layout on bulk edit screens
2025-06-17 11:55:31 +01:00
snipe
fb19985186 Use the same variable for fieldset closing tag
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 11:52:47 +01:00
snipe
ebc6e1221a Merge pull request #17198 from akemidx/ghissue-16241
Fixed #16241 - menu items not getting class="active" on click.
2025-06-17 11:40:56 +01:00
snipe
2b91dcb700 Merge pull request #17216 from grokability/#17215-wrong-logo-check-in-print
Fixed #17215: Check for acceptance logo vs regular logo
2025-06-17 11:38:19 +01:00
snipe
9d2e333fd6 Check for acceptance logo vs regular logo
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 11:36:10 +01:00
Godfrey M
013ad1069c saving certs sooner in the stack 2025-06-16 16:30:00 -07:00
Godfrey M
ec059717f6 adds id to XML textbox, now updates 2025-06-16 16:01:11 -07:00
akemidx
418566db3f fixes the easy ones 2025-06-16 18:41:36 -04:00
snipe
7be9463be6 Merge pull request #17180 from grokability/#12653-added-jobtitle-to-asset-listing
Fixed #12653 - added jobtitle to asset listing
2025-06-16 20:29:01 +01:00
snipe
51712bc7d6 Check for whether any of the models have a fieldset
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 16:40:07 +01:00
snipe
7b889d22d2 Fixed HTML
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 16:14:01 +01:00
snipe
e8aad989ec Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 14:27:47 +01:00
snipe
4006d64d60 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 14:25:59 +01:00
snipe
d792d99375 Removed stray space for favicon
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 03:51:50 +01:00
snipe
b11036a2e5 Fixed #11977 - changed path in backups language
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 03:40:54 +01:00
snipe
01de69a250 Merge pull request #17181 from grokability/fixed-#14542-missing-fullscreen-in-locations
Fixed #14542 - added fullscreen option for location view tabs
2025-06-15 02:57:51 +01:00
snipe
5e1c2e7feb Fixed #14542 - added fullscreen option for location view tabs
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 02:56:07 +01:00
snipe
b842aa11e5 Remove debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 02:25:25 +01:00
snipe
ff01078b60 Fixed #12653 - adds job title to asset listing
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 02:23:53 +01:00
snipe
b1e92293fc Merge pull request #14998 from timoschwarzer/feature/department-manager-in-table
Added #14997: Display department manager in user view and list
2025-06-14 23:51:41 +01:00
snipe
443f69bd82 Merge branch 'develop' into feature/department-manager-in-table 2025-06-14 23:48:52 +01:00
snipe
f4decbf52e Fixed #15480 - adds location to acceptance email
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 23:08:37 +01:00
snipe
bd2c311e4f One more fix to bulk menu
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 22:22:42 +01:00
snipe
2dcab6d0b3 Fixed logic on bulk menu
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 22:21:00 +01:00
snipe
c68a97198f Fixes #8212 - force date format for purchase date
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 20:40:15 +01:00
snipe
2702c3da2b Merge pull request #17178 from grokability/fixes-maintenance-name-on-delete
Fixed #17177 - use maintenance title for delete confirmation
2025-06-14 17:44:21 +01:00
snipe
da06e9afd5 Fixes #17177 - undefined title on asset maintenance
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 17:40:14 +01:00
snipe
1623f13539 Merge pull request #17147 from chrisnovakovic/php_upload_limit-hardening
Docker: harden updating of `php.ini` in entrypoint
2025-06-14 17:11:15 +01:00
snipe
5910982a4f Merge pull request #17176 from grokability/nicer-bulk-options-for-assets
Add logic around menu options
2025-06-14 17:06:49 +01:00
snipe
74630b36b0 Merge pull request #17175 from grokability/fixes/17172-checkin-date
Fixed #17172 - Better handle checkin date overrides
2025-06-14 17:06:30 +01:00
snipe
ace4a5d614 Add logic around menu options
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 17:02:09 +01:00
snipe
0d41947f64 Better handle checkin date overrides
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 16:54:25 +01:00
snipe
78de3b3591 Merge pull request #17174 from grokability/fixes-17163-permissions-on-maintenances
Fixed #17163 - insufficient permissions on editing an asset maintenance
2025-06-14 16:43:27 +01:00
snipe
0a4a6e7ba3 Removed unusued relationship
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 16:37:44 +01:00
snipe
090399b336 Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 16:34:12 +01:00
snipe
47afb15970 Updated HTML
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 16:24:05 +01:00
snipe
b1ba3376aa Validation for completion date
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 16:13:57 +01:00
snipe
8c1e19e77c Fixed breadcrumbs
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 15:53:05 +01:00
snipe
0801d1473c Use forem group div
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:57:55 +01:00
snipe
6d98878c72 Removed redirect
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:31:15 +01:00
snipe
2d404fdadc Fixed breadcrumbs
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:30:52 +01:00
snipe
b264fde165 Show title if it’s an edit
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:30:43 +01:00
snipe
970ff25e5e Added model and company to manufacturer view
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:30:27 +01:00
snipe
9dd3eee65c Pull incorrect check for company scoping
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:04:09 +01:00
snipe
957faa6651 Fixed datepicker prepopulating with start_date
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 14:03:52 +01:00
snipe
cc7dcc6e81 Disallow multiple on editing a maintenance
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 13:39:18 +01:00
snipe
f666cba104 Fixed RB-3991 - override route for non-existent fieldset
Signed-off-by: snipe <snipe@snipe.net>
2025-06-14 12:17:31 +01:00
Marcus Moore
7f35498919 Remove some test cases 2025-06-12 15:56:24 -07:00
Marcus Moore
297205ff91 Tests 2025-06-12 15:55:25 -07:00
Marcus Moore
6f99381d13 Handle accessories 2025-06-12 15:49:38 -07:00
Marcus Moore
45a42b00ad Add test 2025-06-12 15:48:48 -07:00
Marcus Moore
6586858284 Scaffold some test cases 2025-06-12 15:31:32 -07:00
Marcus Moore
19cce15e54 Revert "Method signature change"
This reverts commit a60ffc0702.
2025-06-12 13:54:53 -07:00
Marcus Moore
62b8e4c46f Revert "Inline some values"
This reverts commit 74b7d27408.
2025-06-12 13:54:46 -07:00
Marcus Moore
74b7d27408 Inline some values 2025-06-12 13:49:19 -07:00
Marcus Moore
a60ffc0702 Method signature change 2025-06-12 13:46:05 -07:00
snipe
9b8524ba27 Merge pull request #17109 from akemidx/bug/sc-28718-2
FIXED: Adding Total to Consumable View Page
2025-06-12 21:44:08 +01:00
Marcus Moore
32526d77b8 Add todo 2025-06-12 13:35:22 -07:00
snipe
ba7db8f7b3 Merge pull request #17092 from Godmartinz/fix-bulk_checkout_undeployable
Adds `hasUndeplyableStatus` check to bulk checkout
2025-06-12 21:29:26 +01:00
snipe
04712ad252 Merge pull request #17151 from Godmartinz/bulk-checkout-add-pending-status
Fixed #17028: Allows bulk editing assets with a status type of pending
2025-06-12 21:27:44 +01:00
Marcus Moore
5711a9e148 Fix test 2025-06-12 13:02:26 -07:00
Marcus Moore
a0f40c2dfb Tests 2025-06-12 12:56:24 -07:00
Marcus Moore
34636016eb Improve test 2025-06-12 12:47:03 -07:00
Marcus Moore
95027e329c Formatting 2025-06-12 12:44:29 -07:00
Marcus Moore
c9778a73c7 Wire up category controller 2025-06-12 12:32:34 -07:00
Marcus Moore
628d2a0a0a Flesh out mail contents 2025-06-12 12:12:38 -07:00
Marcus Moore
cc0ff1ec1f Handle missing recipient 2025-06-12 11:53:30 -07:00
snipe
6543540509 Merge pull request #17166 from Godmartinz/item_name_fix_from_account_eula_table
FIXED: #17136 loads item relation properly in account EULA tab
2025-06-12 11:53:51 +01:00
Marcus Moore
cd53fc6318 Scaffold email contents 2025-06-11 16:56:40 -07:00
Godfrey M
4934b6c4da fix query for item relationship to load properly 2025-06-11 11:42:16 -07:00
snipe
59db38524b Merge pull request #17094 from spencerrlongg/snipe-it-17051
Fixes #17051 - Nulling Custom Fields In Bulk Asset Updates
2025-06-11 10:31:28 +01:00
snipe
3f5cfc3a4b Merge pull request #17142 from marcusmoore/fixes/present-on-null-in-expected-checkins-notification
Fixed Expected checkin notification erroring on unknown users
2025-06-11 10:27:49 +01:00
snipe
3443f02c0a Merge pull request #17141 from marcusmoore/fixes/undefined-manages_users_count-on-user
Fixed bad method calls in user index api call
2025-06-11 10:26:13 +01:00
snipe
9a012ca01e Merge pull request #17154 from Robert-Azelis/patch-10
Update user print assigned assets
2025-06-11 10:24:50 +01:00
snipe
509ef34cca Merge pull request #17155 from Robert-Azelis/patch-11
Update location print assigned assets
2025-06-11 10:24:29 +01:00
snipe
49c289a094 Merge pull request #17158 from grokability/changes-default-history-column-visibility
Changed default visibility on history views
2025-06-11 10:19:33 +01:00
snipe
10e5d88fb6 Changed default visibility on history views
Signed-off-by: snipe <snipe@snipe.net>
2025-06-11 10:16:26 +01:00
snipe
ae64fb3fdb Merge pull request #17157 from grokability/fixed-#17138-category-type-case-sensitive
Fixed #17138 - category type was case-sensitive
2025-06-11 10:12:05 +01:00
snipe
cac2fde504 Fixed #17138 - category type in category importer is case sensitive
Signed-off-by: snipe <snipe@snipe.net>
2025-06-11 10:08:59 +01:00
snipe
bb38a96fd1 Merge pull request #17152 from marcusmoore/fixes/handle-category-missing-upon-checkin
Handle potentially missing category upon checkin
2025-06-11 09:36:50 +01:00
Robert-Azelis
79dbcb10c9 Update location print assigned assets
Lets allow user to select print layout: portrait or landscape
Use dedicated logo for PDF documents: acceptance_pdf_logo
2025-06-11 09:22:13 +02:00
Robert-Azelis
7c80fdea58 Update user print assigned assets
Lets allow user to select print layout: portrait or landscape
Use dedicated logo for PDF documents: acceptance_pdf_logo
2025-06-11 09:19:51 +02:00
Godfrey M
5500a42744 change message from error to warning 2025-06-10 15:10:10 -07:00
Godfrey M
ae46264707 removed comment 2025-06-10 15:07:42 -07:00
Godfrey M
f2d8665e54 adds tests 2025-06-10 15:07:04 -07:00
Marcus Moore
db7110d6b2 Remove test 2025-06-10 10:43:23 -07:00
Marcus Moore
4e06b597fe Handle category missing in Consumable 2025-06-10 10:43:02 -07:00
Godfrey M
fe006d05d3 allows to change the status to pending in bulk edit while deployed 2025-06-10 10:41:26 -07:00
Marcus Moore
358b70e280 Handle category missing in Accessory 2025-06-10 10:36:59 -07:00
Marcus Moore
a060dde625 Add failing test 2025-06-10 10:34:25 -07:00
Godfrey M
3cfed72af4 removes undeployables from asset_id array 2025-06-10 10:11:00 -07:00
Chris Novakovic
4c59989236 Docker: harden updating of php.ini in entrypoint
The Docker entrypoint scripts set values for the `upload_max_filesize`
and `post_max_size` directives in `php.ini` based on the value of the
`PHP_UPLOAD_LIMIT` environment variable, subject to the following
restrictions:

* Exactly one file matches `/etc/php/*/apache2/php.ini` (on Ubuntu) or
  `/etc/php*/php.ini` (on Alpine) - if, for example, more than one PHP
  package is installed in the base image, `PHP_UPLOAD_LIMIT` will not be
  honoured.
* The `php.ini` file already sets a non-default value for the
  `upload_max_filesize` or `post_max_size` directives - this is
  currently the case for the configurations inherited from upstream, but
  is not guaranteed. If the default values are relied upon,
  `PHP_UPLOAD_LIMIT` will silently not be honoured (although the script
  output will claim that it is).

Iterate over the lines outputted by `file(1)` so `PHP_UPLOAD_LIMIT` is
honoured in all available `php.ini` files, and set `upload_max_filesize`
and `post_max_size` regardless of whether they already have a value set.
2025-06-10 13:39:46 +01:00
Marcus Moore
6c1adff5c8 Extract translation string 2025-06-09 15:33:39 -07:00
Marcus Moore
9293bdca06 Don't render link for Unknown User 2025-06-09 15:32:07 -07:00
Marcus Moore
beeccbfb44 Handle unknown users gracefully 2025-06-09 15:30:03 -07:00
Marcus Moore
0d3d2e2e78 Fix keyes 2025-06-09 12:55:38 -07:00
Marcus Moore
2af7605451 Add failing tests 2025-06-09 12:55:09 -07:00
snipe
976cc1c86f Fixed second incorrect string
Signed-off-by: snipe <snipe@snipe.net>
2025-06-09 14:00:47 +01:00
snipe
cbbf3aa6c8 Fixed incorrect translation string
Signed-off-by: snipe <snipe@snipe.net>
2025-06-09 13:40:34 +01:00
snipe
4f8ff98d5b Merge pull request #17129 from grokability/fixes#17127-added-note-to-eula-api
Fixed #17127 - added note to EULA info
2025-06-08 15:29:47 +01:00
snipe
d4fe81c290 Fixed #17127 - added note to EULA info
Signed-off-by: snipe <snipe@snipe.net>
2025-06-08 15:27:50 +01:00
snipe
cbdf03aa66 Nicer code formatting
Signed-off-by: snipe <snipe@snipe.net>
2025-06-08 15:18:59 +01:00
snipe
d756670c56 Check for supplier before trying to show
Signed-off-by: snipe <snipe@snipe.net>
2025-06-08 15:17:24 +01:00
snipe
9ef7b0e64a Fixed missing translation string
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 20:41:36 +01:00
snipe
2d7c0f7e5f Merge pull request #16469 from grokability/fix_action_date
Fixed `action_date` in `action_logs`
2025-06-06 17:18:39 +01:00
snipe
ec8ddc197f Merge branch 'develop' into fix_action_date 2025-06-06 17:18:27 +01:00
snipe
b48e56bd46 Merge pull request #17110 from Godmartinz/allow-users-to-dl-eula-from-account
Fixed #17084 - Adds Eula table to User account area with download option
2025-06-06 17:07:19 +01:00
snipe
753ca93371 Merge pull request #17126 from grokability/fixes-#17023-multi-maintenances
Fixes #17023 - added ability to bulk add maintenances
2025-06-06 17:01:50 +01:00
snipe
04f71e7f6a Shorter syntax
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 16:58:10 +01:00
snipe
5d129dd420 Small form fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 16:56:45 +01:00
snipe
1c37c630aa Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 16:26:37 +01:00
snipe
d329d6104e Make supplier_id nullable
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:55:54 +01:00
snipe
482723f3bc Updated blade to use multiple
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:47:07 +01:00
snipe
cbc025b1ff Updated save method to save multiple
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:46:55 +01:00
snipe
bf8ceceabe Add bulk maintenance handler
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:42:14 +01:00
snipe
fba4bba132 Make supplier not required anymore
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:41:41 +01:00
snipe
0ded40c037 Add multiselect to maintenance form
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:41:30 +01:00
snipe
6f486a37ff Added maintenance option to bulk menu
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 15:41:02 +01:00
snipe
1ef822997b Merge pull request #17125 from grokability/fixed-17117-added-translation
Fixed #17117 - use translation for “site default”
2025-06-06 14:04:08 +01:00
snipe
ea66629e98 Fixed #17117 - use translation for “site default”
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 14:03:17 +01:00
snipe
84c9979fe3 Merge pull request #17124 from grokability/add-print-button-to-tables
Added print button to tables
2025-06-06 13:52:22 +01:00
snipe
867a992183 Added print button to tables
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 13:51:12 +01:00
snipe
c3ad7d649c Merge pull request #17122 from grokability/added_highlighting_to_search
Added highlighting on table search
2025-06-06 12:47:31 +01:00
snipe
fc250e228d Added highlighting on table search
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 12:43:47 +01:00
snipe
37e81568ea Merge pull request #17120 from grokability/fixes-11807-datepicker-blade-component
Fixed #11807 - Standardize date-picker
2025-06-06 11:44:54 +01:00
snipe
048d910d5b Use partial
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 11:33:17 +01:00
snipe
b162aba445 Added localization for datepicker
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 11:33:02 +01:00
snipe
974627849b Added datepicker partial
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 11:32:47 +01:00
snipe
e18e9f699e Use blade component in datepicker partial
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 11:32:37 +01:00
snipe
cc9209d2de Removed EOL date and purchase date partials
Signed-off-by: snipe <snipe@snipe.net>
2025-06-06 11:32:13 +01:00
snipe
dd3d264e63 Merge pull request #16675 from Godmartinz/null_location_id_fix
Adds a check for assigned target not being null when creating an asset
2025-06-06 08:30:31 +01:00
snipe
246f1373a8 Merge pull request #16948 from marcusmoore/bug/sc-29181
Require assigned_x to be integer on asset model
2025-06-06 07:57:20 +01:00
snipe
b993f4270e Merge pull request #17114 from grokability/fixes-16934-update-asset-by-id
Fixed #16934 and #17068 - update asset by ID in importer
2025-06-06 07:55:03 +01:00
snipe
6c51ef11b1 Merge pull request #17115 from marcusmoore/17067-limit-cc-if-acceptance-required
Fixed #17067: Allow only sending cc email when acceptance required
2025-06-06 07:41:26 +01:00
Marcus Moore
ae98f6276e Replace reject with declined 2025-06-05 17:24:57 -07:00
Marcus Moore
7424a5987b Add subject 2025-06-05 17:22:31 -07:00
Marcus Moore
2d08749207 Improve readability 2025-06-05 16:56:11 -07:00
Marcus Moore
db50e98ae3 Populate tests 2025-06-05 16:51:44 -07:00
Marcus Moore
7ec0925c69 Scaffold out tests 2025-06-05 16:44:44 -07:00
Marcus Moore
df1361aa43 Scaffold test 2025-06-05 16:42:31 -07:00
akemidx
6db04c86df making fields active. first pass 2025-06-05 18:46:33 -04:00
Marcus Moore
bec80b443c WIP: begin to send email 2025-06-05 15:15:22 -07:00
Marcus Moore
333501fe55 WIP: create mail class 2025-06-05 15:07:19 -07:00
Marcus Moore
063553d4f7 Scaffold scenarios 2025-06-05 14:57:15 -07:00
Marcus Moore
19b9e50281 Update livewire component for alert_on_response 2025-06-05 14:40:19 -07:00
Marcus Moore
2a68b4aeff Update test for updating category with alert_on_response 2025-06-05 14:36:06 -07:00
Marcus Moore
5e25150521 Add another test case 2025-06-05 14:26:56 -07:00
Marcus Moore
cb183d3645 Store alert_on_response_id on CheckoutAcceptance 2025-06-05 14:06:22 -07:00
Marcus Moore
96bce301a0 Add alert_on_response to Category 2025-06-05 13:43:20 -07:00
Marcus Moore
360f5b7538 Add alert_on_response_id to CheckoutAcceptance 2025-06-05 13:10:18 -07:00
Marcus Moore
77234f6580 Extract translation strings 2025-06-05 12:24:46 -07:00
Marcus Moore
088e6af0b5 Remove admin_cc_email validation for admin_cc_always 2025-06-05 12:07:14 -07:00
Lukas Kraic
16fb1018a2 List users code refactoring 2025-06-05 20:05:38 +02:00
Godfrey M
e2e54677ee add auth to api call, gave more specificity to the relationship 2025-06-05 10:59:16 -07:00
Lukas Kraic
ad6fe855a9 Fix code style: Change comments 2025-06-05 18:53:36 +02:00
Lukas Kraic
c50c97d149 Fix code style: Change comments 2025-06-05 18:18:07 +02:00
Lukas Kraic
8b98ae15f0 Fix code style: Add comment 2025-06-05 18:08:34 +02:00
Lukas Kraic
261f84d5f5 Fix code style: Remove comment 2025-06-05 17:55:46 +02:00
Lukas Kraic
29989ac24e Fix code style: Remove empty line after end comment 2025-06-05 13:50:57 +02:00
snipe
7a93e94fa6 Add ID to field list
Signed-off-by: snipe <snipe@snipe.net>
2025-06-05 12:35:30 +01:00
snipe
e33f73fe9f Fixed comment text
Signed-off-by: snipe <snipe@snipe.net>
2025-06-05 11:56:14 +01:00
snipe
6291389df5 Fixed #16934 - update asset by ID in importer
Signed-off-by: snipe <snipe@snipe.net>
2025-06-05 11:53:57 +01:00
snipe
ed817dc414 Merge branch 'develop' into snipe-it-17051 2025-06-05 11:37:17 +01:00
Lukas Kraic
7494fa6bc9 Fix code style: Remove unnecessary end comment from short conditional block 2025-06-05 12:03:42 +02:00
Lukas Kraic
62e50dbe52 Fix code style: Add semantically correct end comments 2025-06-05 11:56:13 +02:00
Lukas Kraic
30c090ba2d Fix code style: Add descriptive end comment for conditional block 2025-06-05 11:45:05 +02:00
Lukas Kraic
7ff82e6043 Delete comments 2025-06-05 11:07:42 +02:00
snipe
2950fb1041 Merge pull request #17108 from Godmartinz/clear-buttons-for-custom-fields
Adds a clear button to custom radio buttons in bulk edit
2025-06-05 10:03:27 +01:00
Lukas Kraic
61d3e2fb49 Fix code style: Add required whitespace in end block comments 2025-06-05 09:23:58 +02:00
Lukas Kraic
fb18c1a0be Fix code style: Remove whitespace in end block comments 2025-06-05 08:59:07 +02:00
Godfrey M
68c082e0dc fix doc blocks 2025-06-04 15:48:35 -07:00
Godfrey M
ee3deb9c63 made eula api route, formatted table, cleaned up code 2025-06-04 15:04:59 -07:00
Marcus Moore
c1505de8d6 Update wording 2025-06-04 13:02:04 -07:00
Marcus Moore
10be434c13 Populate tests 2025-06-04 12:58:49 -07:00
Marcus Moore
92e22eead5 Formatting 2025-06-04 12:51:59 -07:00
Marcus Moore
31db86abd3 Simplify test 2025-06-04 12:51:48 -07:00
Marcus Moore
8e70ff135a Scaffold tests 2025-06-04 12:44:55 -07:00
akemidx
5ec52f7471 beginning of everything. tried some stuff but yea. something is up 2025-06-04 15:10:16 -04:00
Marcus Moore
8bc57f98a5 Split test case 2025-06-04 11:51:29 -07:00
Godfrey M
6f4cee6334 adds Eula tab and count to user account 2025-06-04 11:38:50 -07:00
Godfrey M
6beccc5e60 adds clear ability to custom radio buttons 2025-06-04 10:24:45 -07:00
Godfrey M
fed8e10644 Merge remote-tracking branch 'upstream/develop' into clear-buttons-for-custom-fields 2025-06-04 10:06:59 -07:00
snipe
3c0121c1d0 Merge pull request #17107 from grokability/fixes-16218-add-centos
Fixed #16218 - added centos to the switch case
2025-06-04 15:33:23 +01:00
snipe
02021e3fb9 Fixed #16218 - added centos to the switch case
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 15:27:19 +01:00
Lukas Kraic
ea447365fa Fix Codacy warnings II 2025-06-04 15:40:17 +02:00
Lukas Kraic
60989d6766 Fix Codacy warnings 2025-06-04 15:17:55 +02:00
snipe
a2960dc653 Merge pull request #17103 from grokability/fixes-17102-add-cc-as-search-string-in-settings
Fixed #17102 - added keywords to admin settings search for notifications
2025-06-04 12:12:17 +01:00
snipe
702499dd79 Fixed #17102 - added keywords to admin settings search for notifications
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 12:09:57 +01:00
snipe
482c427e34 Merge pull request #17100 from uberbrady/add_deleted_at_index_to_action_logs
Fixed #16205 - Add an index to deleted_at for those with many deleted action_logs
2025-06-04 11:48:45 +01:00
snipe
67910490bd Merge pull request #17101 from grokability/fixes-16157-added-advanced-search-to
Fixed #16157 - Added advanced search to users
2025-06-04 11:48:24 +01:00
snipe
fdb5ab2293 Added advanced search to users
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 11:46:38 +01:00
Brady Wetherington
092d9d1e42 Add deleted_at index to action_logs for people with many deleted action_logs 2025-06-04 11:23:09 +01:00
snipe
e657f11531 Merge pull request #17099 from grokability/fixes-16240-localization-strings
Fixed #16240 - made additional strings translatable
2025-06-04 10:40:21 +01:00
snipe
9aac183318 Added aria tag for accessibility
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 10:40:05 +01:00
snipe
c8c2867305 Remove unused translation
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 10:39:08 +01:00
snipe
3e1f71026c Fixed #16240 - made additional strings translatable
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 10:37:04 +01:00
snipe
dc562d8c20 Remove error log
Signed-off-by: snipe <snipe@snipe.net>
2025-06-04 08:43:40 +01:00
Lukas Kraic
84ec5aea26 Manager View Feature 2025-06-04 09:07:26 +02:00
Marcus Moore
444c13c6ea Scaffold template 2025-06-03 17:10:15 -07:00
spencerrlongg
12d5e4f7d2 cleanup and test 2025-06-03 19:07:02 -05:00
spencerrlongg
0fb1639915 this works! 2025-06-03 18:06:41 -05:00
Marcus Moore
d01f7cf317 Adhere to admin_cc_always setting 2025-06-03 15:32:11 -07:00
spencerrlongg
03725c8e0c custom field null and filtering 2025-06-03 17:21:07 -05:00
Marcus Moore
3942489d21 Add Test suffix and scaffold test 2025-06-03 14:13:52 -07:00
Marcus Moore
51479c8bbc Scaffold failing test 2025-06-03 13:28:40 -07:00
Marcus Moore
ea3364ab68 Split test case 2025-06-03 13:19:44 -07:00
Godfrey M
cb608d7fd1 testing buttons out 2025-06-03 11:58:57 -07:00
Godfrey M
7129008428 remove testing changes 2025-06-03 11:44:38 -07:00
Godfrey M
3b832f507f fixes status check for bulk checkout 2025-06-03 11:38:59 -07:00
Godfrey M
b5849500f9 add isDeplyable check 2025-06-03 10:49:17 -07:00
snipe
9eaabf95a0 Make version number match tagged version :(
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 11:02:41 +01:00
snipe
724f38abc2 Merge pull request #16998 from kovacs-andras/develop
Bumped container image versions
2025-06-03 06:22:08 +01:00
snipe
0dfc083a91 Removed log error
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 06:07:06 +01:00
snipe
16432f503a Merge pull request #17087 from grokability/#17085-checkin-and-delete-not-nulling-assigned_type
Fixed #17085 - assigned_type not being nulled on asset delete+checkin
2025-06-03 05:53:24 +01:00
snipe
7c9433be5d Added migration to fix existing wonky data
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:37:37 +01:00
snipe
e4ce71ff14 Added time on action date
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:30:53 +01:00
snipe
45c6406ff4 Added console command for fixup
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:30:43 +01:00
snipe
550e2b6bb8 Null both assigned to and assigned type on delete
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:30:30 +01:00
snipe
a7bb890729 Removed action date from array of things to log
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:29:49 +01:00
snipe
3d8f8faf01 Added action_date
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:28:58 +01:00
snipe
55cf5877c4 Updated tests
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 05:28:45 +01:00
Marcus Moore
dec3c4aff3 Improve wording 2025-06-02 17:45:08 -07:00
Marcus Moore
79e00c1191 Require admin_cc_email if admin_cc_always is true 2025-06-02 17:21:58 -07:00
Marcus Moore
12dc33244d Start storing admin_cc_always 2025-06-02 17:20:01 -07:00
Marcus Moore
6e37f945ac Add test helpers 2025-06-02 17:13:37 -07:00
Marcus Moore
d75120000a Add failing tests 2025-06-02 17:11:18 -07:00
Marcus Moore
9e4aab7165 Scaffold tests 2025-06-02 17:05:18 -07:00
Marcus Moore
6bc3209333 Use @checked for inputs 2025-06-02 17:05:00 -07:00
Marcus Moore
054ff42547 Add migration for admin_cc_always 2025-06-02 17:03:14 -07:00
spencerrlongg
11b47b308b front end done, sloppy 2025-06-02 18:39:08 -05:00
Marcus Moore
367ab8ddd5 Add help text 2025-06-02 16:27:04 -07:00
Marcus Moore
4f5d4a0984 Scaffold settings page changes 2025-06-02 16:25:06 -07:00
snipe
40489c53d6 Merge pull request #17078 from grokability/fixes-#17076-validation-on-bulk-submit
Fixed #17076 - Disable optional status ID form field if value is blank/Do Not Change
2025-06-02 22:28:31 +01:00
snipe
93b760d53b Disable form fields if the value is blank
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 22:16:36 +01:00
snipe
e86996bc7e Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 18:15:21 +01:00
snipe
14244f45b6 Duplicates PR #16957
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 18:13:16 +01:00
snipe
1b9d90a322 Added over sixty test
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 17:36:50 +01:00
snipe
736f74d083 Merge pull request #17074 from uberbrady/improve_api_rate_limiting
Fix to rate-limiter on higher rate-limits
2025-06-02 17:25:02 +01:00
Brady Wetherington
8194c6efdb Fix to rate-limiter on higher rate-limits 2025-06-02 17:21:14 +01:00
snipe
aae2a17ad1 Add @amedranogil as a contributor 2025-06-02 15:27:55 +01:00
snipe
f44150668c Merge pull request #17038 from amedranogil/develop
more robust php.ini update.
2025-06-02 15:27:28 +01:00
snipe
6eed2deb09 Merge pull request #17013 from Robert-Azelis/patch-9
API Models - added requestable for API request
2025-06-02 15:26:08 +01:00
snipe
878c6e7031 Merge pull request #17019 from grokability/#15320-status_to_bulk_checkout
Fixed #15320 - added status label to bulk checkout
2025-06-02 15:05:44 +01:00
snipe
1f4a73fab6 Merge pull request #17062 from grokability/add_category_importer
Added category importer
2025-06-02 15:03:31 +01:00
snipe
7a315523fe Improved CSV
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 15:01:16 +01:00
snipe
6f082e662b Fixed weird layout
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 14:52:41 +01:00
snipe
018c981c5a Merge pull request #17042 from marcusmoore/chore/replace-customfield-elements-macro-take-two
Replace customfield_elements form macro take two
2025-06-02 10:18:19 +01:00
snipe
0149773a03 Fixed variable name
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 04:14:51 +01:00
snipe
5d46d90725 Added category importer
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 04:11:54 +01:00
snipe
0544e05f32 Merge pull request #17061 from grokability/add_manufacturer_importer
Added manufacturer importer
2025-06-02 03:05:25 +01:00
snipe
80ff42a41f Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 03:01:56 +01:00
snipe
90b7df45b9 Added tests and support helper
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:55:11 +01:00
snipe
32858b973a Added sample CSV
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:55:00 +01:00
snipe
40ba8d0de1 Fixed “send welcome email” detection
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:54:34 +01:00
snipe
8ddbb4e64f Added manufacturer factory
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:54:21 +01:00
snipe
cc40c48aac Added manufacturers import fields
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:54:03 +01:00
snipe
522ab9e0f5 Added manufacturer importer
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:53:46 +01:00
snipe
97187aa7eb Skip manufacturers on checkout import type
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:53:37 +01:00
snipe
d93a5aa623 Added redirect after import
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:53:09 +01:00
snipe
a5b88982bf Added manufacturer icon
Signed-off-by: snipe <snipe@snipe.net>
2025-06-02 02:52:55 +01:00
snipe
df71bdcada Merge pull request #17044 from marcusmoore/bug/sc-29302
Handle missing location when rendering labels
2025-06-02 02:21:19 +01:00
snipe
51bab2dd26 Merge pull request #17045 from grokability/docker-laravel-log-permissions
Docker: Ensure permissions on Laravel log file
2025-05-30 06:52:31 +01:00
Jeremy Price
ed8da6ad1b Docker: Ensure permissions on Laravel log file
FIXES: https://github.com/grokability/snipe-it/issues/12725

In some of our Docker startups, it was possible for the Laravel log file
to be created with root permissions, causing future errors when the
non-root webapp tries to write to it.

We'll now always chown (and create, if necessary) the log file to the proper
user after running any artisan commands (as root)

We _could_ run them as the proper user via su, but IMO not doing so keeps the
script easier to read, but I'm not married to the approach. I'd still
want to keep the chown command(s) in, because it will also fix the
permissions for anyone who already has this issue.
2025-05-29 17:45:14 -07:00
Marcus Moore
18d0a04efc Avoid dumping pdf contents to test results 2025-05-29 15:05:08 -07:00
Marcus Moore
bb68ed3ad9 Handle asset not having location 2025-05-29 14:49:53 -07:00
Marcus Moore
402ca07aa2 Add failing test 2025-05-29 14:20:15 -07:00
snipe
28dc358df1 Merge pull request #17041 from grokability/improve_locations_and_supplier_api
Small refinements for suppliers and locations API and list view
2025-05-29 21:30:54 +01:00
Marcus Moore
3cf1e9d55d Remove customfield_elements macro 2025-05-29 13:30:23 -07:00
Marcus Moore
82b001ab5f Extract translation strings 2025-05-29 13:29:13 -07:00
Marcus Moore
7b272226ce Inline customfield_elements select 2025-05-29 13:29:13 -07:00
snipe
78d26fb7f6 Removed stray character
Typing is hard :(

Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 21:23:50 +01:00
snipe
930842e685 Removed unused method
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 21:18:19 +01:00
snipe
b938cb42d8 Merge pull request #17040 from marcusmoore/improve-acceptance-reminder-output
Avoid displaying empty table in `SendAcceptanceReminder` command
2025-05-29 21:14:09 +01:00
snipe
4c7b6d130f Added additional search and display fields for suppliers and locations
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 21:13:08 +01:00
Marcus Moore
af57ca4983 Avoid displaying empty table 2025-05-29 11:55:43 -07:00
snipe
40c31a1ad7 Eager load adminuser method
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 17:13:16 +01:00
snipe
7ae4a4177f Added created_by to transformer
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 17:13:05 +01:00
snipe
6efd323fbf Added adminuser method
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 17:12:48 +01:00
snipe
ed9dbcc777 Added created_by to location presenter
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 17:12:33 +01:00
snipe
c2cf7de41b Use presenter for suppliers
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 17:12:16 +01:00
Alejandro M. Medrano Gil
32bd14bd2d more robust php.ini update.
could solve #10830 when setting PHP_UPLOAD_LIMIT environment variable in docker command and/or docker-compose.
2025-05-29 17:46:23 +02:00
snipe
f9cbecdb17 Merge pull request #17037 from grokability/supplier_importer
Added #17036 - suppliers importer
2025-05-29 16:15:59 +01:00
snipe
7bb29a0277 Added sample import CSVs
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:08:17 +01:00
snipe
d5f7579e9f Added columns to suppliers
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:08:01 +01:00
snipe
13fd43c45c Added tests and test support
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:07:51 +01:00
snipe
c08ce901cc Added strings
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:07:19 +01:00
snipe
94bd11d3c9 Added locations and supplier import types
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:07:11 +01:00
snipe
59c6e26b29 Fixed mapping
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 16:06:51 +01:00
snipe
bf7cc404f8 Set correct redirect
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 15:54:37 +01:00
snipe
12a2c71b90 Added icon
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 14:47:59 +01:00
snipe
6e2eeba0f6 Added supplier importer
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 14:47:54 +01:00
snipe
99a739fae3 Merge pull request #17035 from grokability/settings_style_improvements
Fixed #17034 - larger header color box on small views
2025-05-29 13:30:31 +01:00
snipe
0185f61c11 Fixed #17034 - larger header color box on small views
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 13:23:07 +01:00
snipe
783f0c113d Merge pull request #17009 from marcusmoore/chore/replace-user-skin-macro
Replace `skin` and `user_skin` macros with blade component
2025-05-29 13:07:47 +01:00
snipe
75a839cc21 Merge pull request #17010 from marcusmoore/chore/replace-two-factor-select-macro
Replace `two_factor_options` macro
2025-05-29 13:07:23 +01:00
snipe
577b5586b4 Merge pull request #17025 from akemidx/clear_button_on_date_picker
FIXED: Clear Button actually Clearing Dates on Date Picker
2025-05-29 12:37:56 +01:00
snipe
1474a16148 Merge pull request #17024 from akemidx/created_at_date_picker
ADDED: Created At date picker on Custom Reports
2025-05-29 12:37:17 +01:00
snipe
9baa2000e1 Merge pull request #17026 from marcusmoore/bug/translate-email-format
Reference correct translation string
2025-05-29 12:36:08 +01:00
snipe
d0624dbefe Merge pull request #17027 from akemidx/bug/sc-29295
FIXED: Translation strings in Username/Email formats
2025-05-29 12:34:47 +01:00
snipe
ecb6e8d9a9 Fixed route for custom fields index
Signed-off-by: snipe <snipe@snipe.net>
2025-05-29 12:24:22 +01:00
Robert-Azelis
bbb299faf2 Update AssetModelsController.php 2025-05-29 08:42:29 +02:00
snipe
ba3b55cab0 Merge pull request #17022 from marcusmoore/bug/sc-29281
Avoid dividing by zero in DefaultLabel
2025-05-29 02:42:41 +01:00
akemidx
67acca7bc8 fixing two translation strings. 2025-05-28 20:21:05 -04:00
Marcus Moore
e4b33c3b56 Reference correct translation string 2025-05-28 16:36:39 -07:00
akemidx
ed43c73cec clearing the date pickers 2025-05-28 18:48:22 -04:00
akemidx
a6fa795b41 clearing the date pickers 2025-05-28 18:48:08 -04:00
akemidx
3a6bac2e63 date picker 2025-05-28 18:43:24 -04:00
akemidx
8e01c05e42 date picker 2025-05-28 18:43:14 -04:00
akemidx
fabf9281e9 date picker 2025-05-28 18:35:43 -04:00
Marcus Moore
6588d409b8 Add validation 2025-05-28 14:45:03 -07:00
snipe
92a3421a4e Merge pull request #17020 from akemidx/column_persist_on_assigned_assets
FIXED: Column persist on User's Self View of Assigned Assets
2025-05-28 22:44:36 +01:00
Marcus Moore
a42147ff34 Enforce min of .1 for label width and height 2025-05-28 14:05:29 -07:00
Marcus Moore
0df1bc6894 Scaffold test 2025-05-28 14:04:12 -07:00
akemidx
9317076c5e adding cookie for Assigned Assets 2025-05-28 16:39:37 -04:00
snipe
1b5525c51f Added status label to view blade, variable to controller method
Signed-off-by: snipe <snipe@snipe.net>
2025-05-28 20:51:21 +01:00
snipe
6019c80c7b Added blade element
Signed-off-by: snipe <snipe@snipe.net>
2025-05-28 20:50:59 +01:00
Marcus Moore
aaa6cb24d4 Scaffold test 2025-05-28 11:28:23 -07:00
snipe
1ef5ad500a Merge pull request #17017 from grokability/localization/translations-2025-05-28
Updated translations
2025-05-28 18:34:15 +01:00
snipe
9468acedfa Updated languages
Signed-off-by: snipe <snipe@snipe.net>
2025-05-28 18:20:02 +01:00
snipe
6a951b6357 Merge pull request #17012 from Robert-Azelis/patch-8
API Locations - added company_id for API request
2025-05-28 15:23:08 +01:00
snipe
95f7742259 Removed extra a href
Signed-off-by: snipe <snipe@snipe.net>
2025-05-28 15:21:23 +01:00
snipe
9f795306e5 Merge pull request #17014 from grokability/fix_breadcrumb_crash
Manually add API headers
2025-05-28 15:19:05 +01:00
snipe
6feaff1e7b Removed blade::render
Signed-off-by: snipe <snipe@snipe.net>
2025-05-28 15:12:14 +01:00
Robert-Azelis
9a168354ae Update AssetModelsController.php - added requestable for API request 2025-05-28 15:50:19 +02:00
Robert-Azelis
309d242c4d Update LocationsController.php - added company_id for API request 2025-05-28 15:45:22 +02:00
snipe
5c174f829e Merge pull request #16986 from grokability/api_throttle_headers
Fixed  #16961 - Manually add API headers
2025-05-28 13:47:16 +01:00
Marcus Moore
3c428f2d7b Inline two_factor_options macro 2025-05-27 16:40:08 -07:00
Marcus Moore
6833716576 Remove skin macro 2025-05-27 16:22:43 -07:00
Marcus Moore
bd374d031a Improve component name 2025-05-27 16:22:28 -07:00
Marcus Moore
ef26f48f60 Adapt for regular "skin" macro 2025-05-27 16:21:34 -07:00
Marcus Moore
b07f8525db Fix swapped yellow/yellow-dark 2025-05-27 16:13:05 -07:00
Marcus Moore
9f062701fa Add semi-colon 2025-05-27 16:03:22 -07:00
Marcus Moore
2c452daddf Replace user_skin macro with blade component 2025-05-27 16:00:47 -07:00
snipe
53a82d3f4d Merge pull request #17007 from marcusmoore/bug/sc-29278
Ensure boolean returned from method with boolean return type
2025-05-27 22:00:04 +02:00
Marcus Moore
b5b8816279 Avoid returning null from method that should return a boolean 2025-05-27 11:37:48 -07:00
snipe
7bc4127e8c Removed dupe header
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 15:01:54 +01:00
snipe
06158cc413 Add timestamp header
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 14:58:57 +01:00
snipe
cb49e7c9a6 Updated comments
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 14:32:47 +01:00
snipe
1822027a8f Extend the built-in ThrottleRequests middleware from Laravel
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 14:04:24 +01:00
snipe
c8dabc25e3 Added comment
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 14:03:56 +01:00
snipe
f2b10eeee8 Re-do the initial change :(
Signed-off-by: snipe <snipe@snipe.net>
2025-05-27 13:00:32 +01:00
snipe
4b52e1471c Remove unused use statement after refactor
Signed-off-by: snipe <snipe@snipe.net>
2025-05-26 18:45:21 +01:00
snipe
f6bba03375 Fixed dupe semicolon
Signed-off-by: snipe <snipe@snipe.net>
2025-05-26 13:50:19 +01:00
snipe
b3813a7121 Refactorered limiting headers
Signed-off-by: snipe <snipe@snipe.net>
2025-05-26 13:48:50 +01:00
snipe
eb2a1396ca Merge pull request #16999 from grokability/api_audt_fix
Better messaging when an asset fails validation on quick scan
2025-05-26 14:17:14 +02:00
snipe
0fae18c4ba Better handle missing asset payload because of RMB
Signed-off-by: snipe <snipe@snipe.net>
2025-05-26 13:14:28 +01:00
Andras Kovacs
25ac83e944 Bumped container image versions 2025-05-26 13:07:51 +02:00
snipe
a82e65e190 Add @Tinyblargon as a contributor 2025-05-26 11:09:12 +01:00
snipe
187bb90de0 Merge pull request #16993 from Tinyblargon/fix-16992
fix: `PHP_UPLOAD_LIMIT` not set for PHP 8.3
2025-05-26 12:08:36 +02:00
snipe
293648582a Improvements to API headers
Signed-off-by: snipe <snipe@snipe.net>
2025-05-26 10:52:14 +01:00
Tinyblargon
51a306993c fix: PHP_UPLOAD_LIMIT not set for PHP 8.3 2025-05-25 16:23:17 +02:00
snipe
ec1851fa84 More small carbon fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 19:27:58 +01:00
snipe
bbe748dbd3 Removed noisy log
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 19:05:39 +01:00
snipe
406e8c5874 Added test
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 18:25:36 +01:00
snipe
a4f71a9f0a Manually add API headers
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 18:25:29 +01:00
snipe
3748498523 Merge pull request #16985 from grokability/move_faker_take_2
Moved faker out of dev reqs for seeding
2025-05-23 18:34:45 +02:00
snipe
49d11103f7 Fixed test namespace
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 17:28:40 +01:00
snipe
dce9060820 Moved faker out of dev reqs for seeding
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 17:28:00 +01:00
snipe
de6206ce78 Merge pull request #16982 from grokability/fixes#16958-expiring-assets-unarchived
Fixed #16958 - exclude archived assets from expiring assets report
2025-05-23 15:51:27 +02:00
snipe
a181ba308a Fixed #16958 - exclude archived assets from expiring assets report
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 14:48:54 +01:00
snipe
5a9ac01cf8 Set audit warnings as int
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 14:30:56 +01:00
snipe
a5cd306b1d Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 13:12:30 +01:00
snipe
57d3abab5f Merge pull request #16979 from marcusmoore/bug/sc-29178
Improved creator on accessory show page
2025-05-23 14:05:28 +02:00
snipe
b6eb3185d5 Merge pull request #16968 from marcusmoore/bug/sc-29233
Fixed potential slack webhook setting inconsistencies
2025-05-23 14:05:01 +02:00
snipe
286f78778c Merge pull request #16980 from uberbrady/add_new_checkin_checkout_counters_tests_rebased
New tests for checkin/checkout counters
2025-05-23 13:55:38 +02:00
Brady Wetherington
4b95790e2f WIP: new tests for checkin/checkout counters
note that the test isnt going to work

More WIP for checkout counters

Got all tests passing
2025-05-23 12:37:29 +01:00
snipe
97883971f7 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 10:43:32 +01:00
snipe
b3c12d4ee6 Wider text fields
Signed-off-by: snipe <snipe@snipe.net>
2025-05-23 10:42:24 +01:00
Marcus Moore
bea426d8e6 Add test 2025-05-22 16:54:52 -07:00
Marcus Moore
be60370eae Add test 2025-05-22 16:33:45 -07:00
Marcus Moore
7cc216eb38 Extract variable 2025-05-22 15:05:24 -07:00
Marcus Moore
8c90efe745 Add authorization check 2025-05-22 15:02:14 -07:00
snipe
4b9f4423f6 Merge pull request #16932 from marcusmoore/fixes/webhook-checkin-checkout-fix
Improve notifications
2025-05-22 10:53:43 +01:00
Marcus Moore
932b589bd9 Do not show section if user doesn't exist 2025-05-21 17:28:41 -07:00
Marcus Moore
ce18b91b03 Add translation 2025-05-21 16:49:15 -07:00
Marcus Moore
5f883310b5 Improve display of user on accessory show page 2025-05-21 16:48:26 -07:00
Marcus Moore
69cc46c2b8 Improve method name 2025-05-20 16:41:52 -07:00
Marcus Moore
dc6951f341 Improve method name 2025-05-20 16:28:49 -07:00
Marcus Moore
f7ed336f99 Remove typehint 2025-05-20 16:26:05 -07:00
Marcus Moore
47f287c031 Fix assertion 2025-05-20 16:25:22 -07:00
Marcus Moore
5739393f8d Check to make sure settings exist before attempting to update them 2025-05-20 12:45:23 -07:00
Marcus Moore
13956254ce Add migration to fix webhook settings 2025-05-20 12:27:19 -07:00
Marcus Moore
d6b69c8cc2 Fix webhook selected value in mount method 2025-05-20 12:00:58 -07:00
Marcus Moore
7a79fd18f0 Bind the selected webhook and avoid clearing from database 2025-05-20 11:40:44 -07:00
snipe
e3ffe79c4c Merge pull request #16962 from Godmartinz/audit_notifications_fix
Added dynamic properties to audit notifications
2025-05-20 12:34:33 +02:00
Marcus Moore
043325b966 Merge branch 'develop' into fixes/webhook-checkin-checkout-fix
# Conflicts:
#	app/Listeners/CheckoutableListener.php
2025-05-19 14:41:05 -07:00
Marcus Moore
a2696b799f Standardize test names 2025-05-19 14:33:18 -07:00
snipe
eb8ef37808 Merge pull request #16963 from Godmartinz/fix-double-scroll-bar-permission-groups
Removes double scrollbar from groups
2025-05-19 20:32:30 +02:00
Godfrey M
8416c6df05 removes double scrollbar 2025-05-19 11:21:52 -07:00
Godfrey M
3aa4814342 allow dynamic assignment to audit notifications 2025-05-19 11:08:03 -07:00
snipe
186f322bb5 Merge pull request #16953 from uberbrady/fix_checkin_emails
Make checkin emails not send when not configured to be
2025-05-16 11:14:31 +02:00
Brady Wetherington
28e2e7c924 Get rid of more editorialization 2025-05-16 10:57:34 +02:00
Brady Wetherington
97351028b5 Get rid of editorializing in comments 2025-05-16 10:56:36 +02:00
Brady Wetherington
0ef0863b59 Make checkin emails not send unless the send-emails attribute is set on the category 2025-05-16 10:48:17 +02:00
Marcus Moore
2e80b4ace8 WIP 2025-05-15 17:50:07 -07:00
Marcus Moore
0d896c2ef6 Extract method 2025-05-15 17:44:27 -07:00
Marcus Moore
dc9df04237 WIP 2025-05-15 17:40:51 -07:00
Marcus Moore
b469a64db3 WIP 2025-05-15 17:35:28 -07:00
Marcus Moore
73f19ff4e7 Scaffold tests 2025-05-15 17:30:06 -07:00
Marcus Moore
22b4fac3ee Formatting 2025-05-15 17:24:56 -07:00
Marcus Moore
c97884c8b0 WIP 2025-05-15 16:59:10 -07:00
Marcus Moore
b972fb514a Remove type hint 2025-05-15 16:54:46 -07:00
Marcus Moore
a3871bd1f2 WIP 2025-05-15 16:47:29 -07:00
Marcus Moore
2069f99b2b WIP 2025-05-15 16:42:20 -07:00
Marcus Moore
882d55fd09 Improve tests 2025-05-15 16:31:07 -07:00
Marcus Moore
68ef975726 WIP 2025-05-15 16:22:31 -07:00
Marcus Moore
0685ff3818 WIP 2025-05-15 16:12:51 -07:00
Marcus Moore
b28839d907 Scaffold tests 2025-05-15 15:57:52 -07:00
Marcus Moore
7f0a947de4 WIP 2025-05-15 15:43:57 -07:00
Marcus Moore
c8fc4afe65 Fix method name 2025-05-15 15:31:18 -07:00
Marcus Moore
3b7162cb02 Improve test setup 2025-05-15 15:30:16 -07:00
Marcus Moore
4245456382 Scaffold more tests 2025-05-15 15:27:15 -07:00
Marcus Moore
cdf43e31e2 Remove randomness from factory 2025-05-15 15:14:01 -07:00
Marcus Moore
f19d6b3c52 Scaffold tests 2025-05-15 15:13:50 -07:00
snipe
ca66e29072 Merge pull request #16946 from marcusmoore/bug/sc-29166
Gracefully handle error when editing of soft deleted users
2025-05-15 22:46:48 +02:00
Marcus Moore
6ff76a12f8 Strengthen test scenario 2025-05-15 13:39:27 -07:00
Marcus Moore
8b13997597 Merge branch 'develop' into fixes/webhook-checkin-checkout-fix
# Conflicts:
#	app/Listeners/CheckoutableListener.php
2025-05-15 13:38:20 -07:00
Marcus Moore
9600adee6b Don't allow viewing edit page if user soft deleted 2025-05-15 11:14:15 -07:00
snipe
6eb3819492 Apply correct unique custom field validation for audit API
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 18:31:49 +02:00
snipe
95226f87bc Use same logic for auditStore for validating unique fields
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 18:02:23 +02:00
snipe
ee3ae803b9 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 17:51:28 +02:00
snipe
057667c425 Removed duplicate unique display in table
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 17:10:47 +02:00
Marcus Moore
02fa7daa1d Require assigned_x to be integer on asset model 2025-05-14 14:13:07 -07:00
Marcus Moore
2a2acf6d1c Add failing test 2025-05-14 14:09:48 -07:00
Marcus Moore
f644642fac Add comment 2025-05-14 12:48:32 -07:00
Marcus Moore
3374a9e5a9 Add assertion 2025-05-14 12:48:11 -07:00
Marcus Moore
d3a74a5740 Use route bound user instead of re-querying 2025-05-14 12:46:34 -07:00
Marcus Moore
c458cc904a Add failing test 2025-05-14 12:45:55 -07:00
snipe
5c7b74e17e Merge pull request #16943 from grokability/small-tweaks-to-status-label-text
Clearer text on status label types
2025-05-14 17:35:40 +02:00
snipe
e07b0f65a1 Clearer text on status label types
Signed-off-by: snipe <snipe@snipe.net>
2025-05-14 17:33:12 +02:00
snipe
b06fd5bbca Merge pull request #16942 from uberbrady/quick_temp_fix_notifications
A quick check to make sure that webhooks still fire when email is off
2025-05-14 16:21:37 +02:00
Brady Wetherington
6306f78fe0 A quick check to make sure that webhooks still fire when email is off 2025-05-14 16:14:03 +02:00
snipe
da4bce0c89 Fixed typo
Signed-off-by: snipe <snipe@snipe.net>
2025-05-14 15:02:43 +02:00
snipe
053e815206 Add @JassonCordones as a contributor 2025-05-14 15:02:43 +02:00
snipe
ab1053ecda Merge pull request #16916 from marcusmoore/bug/sc-29153
Handle potential hard exception in Asset@getImageUrl method
2025-05-14 14:52:33 +02:00
snipe
9a61a3391b Merge pull request #16921 from marcusmoore/chore/locale-select
Replace locales macro
2025-05-14 14:52:02 +02:00
snipe
d3c19e28ec Merge pull request #16918 from marcusmoore/bug/sc-29151
Handle displaying deleted creator of an accessory
2025-05-14 13:36:08 +02:00
snipe
87115f2e50 Merge branch 'develop' into bug/sc-29151 2025-05-14 13:35:38 +02:00
Marcus Moore
5f7aadfba0 Ensure CC emails are always sent for assets 2025-05-13 17:56:35 -07:00
Marcus Moore
e954e066b4 Scaffold tests 2025-05-13 17:41:19 -07:00
Marcus Moore
e336182d79 Formatting 2025-05-13 14:45:09 -07:00
Marcus Moore
3eb0743446 Separate checking for sending email and webhook notifications 2025-05-13 14:43:16 -07:00
Marcus Moore
95c1c37ab1 Invert method 2025-05-13 14:25:03 -07:00
Marcus Moore
a7054f0b1e Improve method name 2025-05-13 14:20:42 -07:00
Marcus Moore
e4bfabfabe Add failing tests 2025-05-13 14:12:51 -07:00
snipe
6b56929a06 Null operator for missing created_by record
Signed-off-by: snipe <snipe@snipe.net>
2025-05-13 20:44:25 +02:00
Marcus Moore
e3642bb513 Remove unneeded div 2025-05-12 17:07:17 -07:00
Marcus Moore
ca7c416e19 Remove the replaced locales form macro 2025-05-12 16:35:40 -07:00
snipe
ac6d964e28 Merge pull request #16920 from marcusmoore/fixes/remove-outline-from-label
Remove logo outline from L7162_B
2025-05-12 23:04:49 +01:00
Marcus Moore
e2772c816d Remove label logo outline from L7162_B label 2025-05-12 13:58:13 -07:00
Marcus Moore
916f7401f3 Handle creator being deleted 2025-05-12 12:30:32 -07:00
Marcus Moore
b53a71d523 Add failing test 2025-05-12 12:30:06 -07:00
Marcus Moore
510a2b0889 Formatting 2025-05-12 12:03:15 -07:00
Marcus Moore
73057454c6 Fix bug in getImageUrl method 2025-05-12 11:56:40 -07:00
Marcus Moore
5a6cf2a296 Add test for existing functionality 2025-05-12 11:44:25 -07:00
snipe
9e3e04521e Merge pull request #16900 from marcusmoore/fixes/user-full-name-accessor
Handle settings not being available in full name accessor
2025-05-10 12:26:19 +01:00
snipe
65dfbd02fe Use develop branch
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 21:00:55 +01:00
snipe
649ab53320 Updated codacy link
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 21:00:18 +01:00
snipe
9250624f79 Merge pull request #16909 from grokability/fixes-#16554-category-delete
Fixed #16554 - Added models to deletable check
2025-05-09 19:23:35 +01:00
snipe
995e2090f5 Added/updated tests
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 19:17:53 +01:00
snipe
9b91584776 Added models to deletable check
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 19:17:48 +01:00
snipe
8fd97ea501 Merge pull request #16908 from grokability/bug/sc-28724
Fixed #16535 - more info to side rail in accessories
2025-05-09 18:03:05 +01:00
snipe
a80b9ab362 Fixed #16535 - more info to side rail in accessories
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 17:48:02 +01:00
snipe
556e1081b3 Added two more selectors for byod
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 17:23:08 +01:00
snipe
b070916f0b Merge pull request #16907 from grokability/add_ids_to_menus
Fixed #16456 - added ids to sidenav options and bod
2025-05-09 17:22:19 +01:00
snipe
940caf14b0 Added ids to menu items
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 17:09:59 +01:00
snipe
76da1d6663 Added class to checkbox
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 17:04:19 +01:00
snipe
bafff9020a Merge pull request #16611 from Godmartinz/MS_teams_deprecation_update
Reworked MS Teams deprecation warnings and notifications visibility
2025-05-09 16:38:25 +01:00
snipe
0d5dca6456 Fixed #16690 - fallback to category image if no model image is present
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 14:46:27 +01:00
snipe
0f9b7119c0 Merge pull request #16905 from grokability/fixes_#16901
Fixed #16901 - use default currency for asset maintenance cost
2025-05-09 12:45:46 +01:00
snipe
d4181549e8 Fixes #16901 - use default currency for maintenance cost display
Signed-off-by: snipe <snipe@snipe.net>
2025-05-09 12:43:49 +01:00
Marcus Moore
d57f56e44f Handle settings not being available 2025-05-08 16:20:26 -07:00
Marcus Moore
f7777ca8a5 Replace Form::locales on user setup 2025-05-08 15:38:49 -07:00
Marcus Moore
fce5530bc7 Replace Form::locales on bulk edit users page 2025-05-08 15:31:41 -07:00
Marcus Moore
ad1530e9ff Replace Form::locales on user edit page 2025-05-08 15:17:15 -07:00
Marcus Moore
abb2dcbbe4 Replace Form::locales on localization page 2025-05-08 15:05:48 -07:00
Marcus Moore
437499c5df Fix input name 2025-05-08 15:04:45 -07:00
Marcus Moore
f739c2c84a Introduce locale select component and make replacement on profile page 2025-05-08 15:02:35 -07:00
snipe
7d9b87f059 Merge pull request #16898 from marcusmoore/chore/form-radio-replacement
Replaced Form::radio helpers
2025-05-08 20:50:29 +01:00
Marcus Moore
c157f4190e Replace Form::radio in location partial 2025-05-08 12:25:48 -07:00
Marcus Moore
9357eca1cd Replace Form::radio on asset checkin page 2025-05-08 12:16:55 -07:00
snipe
40c65a07a4 Merge pull request #16896 from grokability/removed_seat_number
Removed seat "name" from licenses seats API/UI response
2025-05-08 17:57:40 +01:00
snipe
13521bcf75 Removed seat “name” from license seats API/UI
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 17:37:27 +01:00
snipe
1c09dc139a Undo previous change
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 16:40:24 +01:00
snipe
d5f955b1e0 License seats are not numbered correctly [sc-29113]
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 16:25:49 +01:00
snipe
9e6e8f0931 Moved incomplete test marker
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 16:22:09 +01:00
snipe
c93ef30801 Ignore flaky test
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 15:43:40 +01:00
snipe
3e0dec4856 Fixed #16893 - more specific upload failure text
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 15:38:38 +01:00
snipe
0b167f5f6f Grab location uploads from backup
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 15:22:26 +01:00
snipe
f6b21fdb82 Merge pull request #16895 from grokability/fixed_#16863_custom_fields_validation
Fixed #16863 - better handle custom fields validation when unique but not required
2025-05-08 15:09:04 +01:00
snipe
f151628808 Merge pull request #16894 from grokability/resolve-webserver-permissions
Fix webserver/user file permissions issue
2025-05-08 15:08:40 +01:00
snipe
e44aad0328 Fixed typeos 2025-05-08 15:08:14 +01:00
snipe
1881054c92 Fixed #16863 - better handle unique not required custom field redirects
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 15:00:43 +01:00
Jeremy Price
f7533c5e41 Fix webserver/user file permissions issue
Fixes https://github.com/grokability/snipe-it/issues/16777

We weren't adding the webserver user to the app-user's group, which was
a problem for the webserver trying to write to the log file if it had
been created by a user-owned process (like a cron) or the installation
script chown-ing everything... even though the log file was created 664

This would often present in mysterious ways. In the linked case, trying
to upload a cvs for import would fail with an unhelpful message, because
the actual error is swallowed in the generic error handler for the page.

I've filed an issue to hopefully help with that: https://github.com/grokability/snipe-it/issues/16893

Used this opportunity to condense some logic that was
identical between architectures,
2025-05-08 13:55:23 +02:00
snipe
f181e0fa55 Merge pull request #16877 from marcusmoore/bug/sc-29012
Allow updating asset model image via api
2025-05-08 06:27:49 +01:00
snipe
b04efdfefc Merge pull request #16889 from grokability/add_updated_range_to_custom_report
Added #16887 - last updated date range for custom report
2025-05-08 06:27:32 +01:00
snipe
352b935dee Merge pull request #16884 from marcusmoore/bug/sc-29097
Removed `2fa_authed` from session upon logout
2025-05-08 06:23:26 +01:00
snipe
0ba3b9975a Added #16887 - last updated date range for custom report
Signed-off-by: snipe <snipe@snipe.net>
2025-05-08 06:21:06 +01:00
Marcus Moore
cc06187f31 Remove 2fa_authed from session upon logout 2025-05-07 14:04:33 -07:00
snipe
a916767392 Show the QR code on the asset page regardless of label settings
Signed-off-by: snipe <snipe@snipe.net>
2025-05-07 11:34:21 +01:00
snipe
1c57bfaa39 Small cosmetic change to offset
Signed-off-by: snipe <snipe@snipe.net>
2025-05-07 11:28:01 +01:00
snipe
4a5adeb661 Fixed #16866 - use singular translation for custom report
Signed-off-by: snipe <snipe@snipe.net>
2025-05-07 10:55:28 +01:00
snipe
01f9772291 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-05-07 10:37:10 +01:00
snipe
960b3aebed Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-05-07 10:24:07 +01:00
Marcus Moore
d75de73867 Allow updating asset model image via api 2025-05-06 17:13:23 -07:00
snipe
e75df97902 Merge pull request #16876 from grokability/switch-back-to-multiarch-docker-with-emulation
Move back to multiarch builds with emulation (for now)
2025-05-06 21:39:23 +01:00
Jeremy Price
5be14ec750 Move back to multiarch builds with emulation (for now)
Turns out it's not straightforward to have multiarchitecture images
within the same namespace, if you want to run each architecture's build
on native runners.

While we work on getting that going, we're moving back to
build-everything-on-intel-runners-with-emulation-for-arm

it means slowwwww arm builds, but it also means we should get our images
straightened out
2025-05-06 22:27:53 +02:00
snipe
717a82f46a Dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-05-06 21:27:02 +01:00
snipe
e40038900b Merge pull request #16875 from ubc-cpsc/bugfix/CVE-2025-46734
Fixes CVE-2025-46734: league/commonmark contains a XSS vulnerability in Attributes extension
2025-05-06 19:19:09 +01:00
Joël Pittet
099eabc240 Fixes CVE-2025-46734 2025-05-06 11:01:45 -07:00
snipe
3a4fa35398 Merge pull request #16874 from grokability/clone_breadcrumb_fix
Fixed breadcrumbs for cloning
2025-05-06 16:42:52 +01:00
snipe
500d6a0cc2 Merge pull request #16873 from grokability/redirect_on_audit
Redirect options on audit
2025-05-06 16:39:29 +01:00
snipe
38e5bf71bc Fixed tests
Signed-off-by: snipe <snipe@snipe.net>
2025-05-06 16:36:09 +01:00
Chris Olin
248a05a916 adds support for continuous 53mm and 0.59in printers 2025-05-06 11:26:52 -04:00
Chris Olin
2c141579dd adds support for GenericTape label printers, includes class for 53mm tape printer 2025-05-06 11:26:40 -04:00
snipe
45ff195f11 Fixed breadcrumbs for cloning
Signed-off-by: snipe <snipe@snipe.net>
2025-05-06 16:17:02 +01:00
snipe
ce543c8179 Use consistent icon
Signed-off-by: snipe <snipe@snipe.net>
2025-05-06 16:12:15 +01:00
snipe
5c11a8c1e0 Modified helper
Signed-off-by: snipe <snipe@snipe.net>
2025-05-06 16:12:06 +01:00
snipe
f013a4c5ea Merge pull request #16871 from grokability/one-more-docker-fix
One more docker repo name fix
2025-05-06 15:16:48 +01:00
Jeremy Price
194a22452e stop building intel image in arm server 2025-05-06 16:10:44 +02:00
Jeremy Price
930a6a2ac8 One more docker repo name fix
i feel like i'm taking crazy pills

also right-naming the arm builds again
2025-05-06 16:06:13 +02:00
snipe
444dea8b42 Merge pull request #16868 from grokability/fix-dockerhub-references
OMG fix 2 more (only 1 active) dockerhub repo references
2025-05-06 10:15:28 +01:00
Jeremy Price
6cc3f69c2a OMG fix 2 more (only 1 active) dockerhub repo references 2025-05-06 07:54:20 +02:00
snipe
36f3834ca5 Merge pull request #16865 from ntaylor-86/fixes/alerts-enabled-new-install
Fixed #16815: Avoids potential error when settings table is empty
2025-05-06 00:59:52 +01:00
Nathan Taylor
d4b73b4fb9 Avoids potential error when alerts table is empty
Updates the Kernel to use the null-safe operator
when accessing the alerts_enabled setting. This prevents
a potential error if the settings object is null.
2025-05-06 09:47:33 +10:00
snipe
b37f488117 Merge pull request #16864 from marcusmoore/update-contributors
Added missing contributors
2025-05-05 22:18:11 +01:00
snipe
ee0a9e834a Fixed BulkDeleteAssetsTest test
Signed-off-by: snipe <snipe@snipe.net>
2025-05-05 22:12:08 +01:00
snipe
c776e0e7e9 Merge pull request #16851 from grokability/fix-dockerhub-repo
Fix dockerhub repo we're pushing to, and arm build names
2025-05-05 22:03:58 +01:00
Jeremy Price
54d3193b6f run arm builds on arm 2025-05-05 22:55:38 +02:00
Marcus Moore
326657c709 contributors:generate 2025-05-05 13:30:49 -07:00
Marcus Moore
c72e86ea2e Update casing for contributor 2025-05-05 13:29:05 -07:00
Marcus Moore
34b1ca29d3 Remove duplicate contributor (QveenSi) 2025-05-05 13:28:25 -07:00
Marcus Moore
1dc876a436 docs: add @austinsasko as a contributor 2025-05-05 13:19:37 -07:00
Marcus Moore
b28bc2c500 docs: add @drexljo as a contributor 2025-05-05 13:18:53 -07:00
Marcus Moore
c38d98b00a docs: add @jfwiebe as a contributor 2025-05-05 13:18:29 -07:00
Marcus Moore
7b83df088b docs: add @JemCdo as a contributor 2025-05-05 13:18:29 -07:00
Marcus Moore
c8b1240665 docs: add @jjasghar as a contributor 2025-05-05 13:18:28 -07:00
Marcus Moore
8a9d6bbdca docs: add @pottom as a contributor 2025-05-05 13:18:27 -07:00
Marcus Moore
6859b36e7c docs: add @dasjoe as a contributor 2025-05-05 13:18:26 -07:00
Marcus Moore
f0073c1528 docs: add @gl-pup as a contributor 2025-05-05 13:18:26 -07:00
Marcus Moore
f6b7e621b7 docs: add @gastamper as a contributor 2025-05-05 13:18:25 -07:00
Marcus Moore
108a0179ca docs: add @LeafedFox as a contributor 2025-05-05 13:18:24 -07:00
Marcus Moore
2baf65aa62 docs: add @m4us1ne as a contributor 2025-05-05 13:18:24 -07:00
Marcus Moore
74c4e9665e docs: add @lopezio as a contributor 2025-05-05 13:18:23 -07:00
Marcus Moore
ae9c22f327 docs: add @loganswartz as a contributor 2025-05-05 13:18:22 -07:00
Marcus Moore
37bca6febd docs: add @sniff122 as a contributor 2025-05-05 13:18:22 -07:00
Marcus Moore
30196793bd docs: add @KorvinSzanto as a contributor 2025-05-05 13:18:21 -07:00
Marcus Moore
3060282ffb docs: add @juhotaipale as a contributor 2025-05-05 13:18:20 -07:00
Marcus Moore
d63bba8db7 docs: add @juanfont as a contributor 2025-05-05 13:18:20 -07:00
Marcus Moore
2c12ee01a0 docs: add @CalvinSchwartz as a contributor 2025-05-05 13:18:19 -07:00
Marcus Moore
aa76424a74 docs: add @byronwolfman as a contributor 2025-05-05 13:18:18 -07:00
Marcus Moore
9188d6229e docs: add @benperiton as a contributor 2025-05-05 13:18:18 -07:00
Marcus Moore
5555f32ffe docs: add @arukompas as a contributor 2025-05-05 13:18:17 -07:00
Marcus Moore
59062980ff docs: add @hex128 as a contributor 2025-05-05 13:18:16 -07:00
Marcus Moore
f4aac5f0b7 docs: add @disc as a contributor 2025-05-05 13:18:16 -07:00
Marcus Moore
5c167aa2a9 docs: add @AlexanderWPapyrus as a contributor 2025-05-05 13:18:15 -07:00
Marcus Moore
8e1eed498e docs: add @MelonSmasher as a contributor 2025-05-05 13:18:14 -07:00
Marcus Moore
e449f39ea6 docs: add @fabiang as a contributor 2025-05-05 13:16:43 -07:00
Marcus Moore
97171e0e1c docs: add @Jarli01 as a contributor 2025-05-05 13:16:42 -07:00
Marcus Moore
5935ca4664 docs: add @dkmansion as a contributor 2025-05-05 13:16:42 -07:00
Marcus Moore
21c88cd311 docs: add @splashx as a contributor 2025-05-05 13:16:41 -07:00
Marcus Moore
5c786d8b70 docs: add @corydlamb as a contributor 2025-05-05 13:09:28 -07:00
Marcus Moore
d718d210ed docs: add @bricelabelle as a contributor 2025-05-05 13:09:27 -07:00
Marcus Moore
f50c5d22b8 docs: add @bmkalle as a contributor 2025-05-05 13:09:27 -07:00
Marcus Moore
c36f9a432e docs: add @terwey as a contributor 2025-05-05 13:09:26 -07:00
Marcus Moore
65b6b02b1d docs: add @xWyatt as a contributor 2025-05-05 12:54:08 -07:00
Marcus Moore
9c65d7c057 docs: add @Wouter0100 as a contributor 2025-05-05 12:54:08 -07:00
Marcus Moore
eab07834cf docs: add @valentyntu as a contributor 2025-05-05 12:54:07 -07:00
Marcus Moore
eb38f33baf docs: add @viclou as a contributor 2025-05-05 12:54:06 -07:00
Marcus Moore
462f9f2f39 docs: add @yannikp as a contributor 2025-05-05 12:54:05 -07:00
Marcus Moore
1021ccb230 docs: add @timwsuqld as a contributor 2025-05-05 12:54:05 -07:00
Marcus Moore
84e9a3a7d6 docs: add @p3nj as a contributor 2025-05-05 12:54:04 -07:00
Marcus Moore
c0060b3625 docs: add @sreyemnayr as a contributor 2025-05-05 12:54:03 -07:00
Marcus Moore
197aa12c61 docs: add @octobunny as a contributor 2025-05-05 12:54:02 -07:00
Marcus Moore
dfb2959751 docs: add @nixn as a contributor 2025-05-05 12:54:01 -07:00
Marcus Moore
95fb4f0e45 docs: add @rcmcdonald91 as a contributor 2025-05-05 12:54:00 -07:00
Marcus Moore
b2c729b7b8 docs: add @owalerys as a contributor 2025-05-05 12:53:59 -07:00
Marcus Moore
fe9b224a44 docs: add @nunomaduro as a contributor 2025-05-05 12:53:59 -07:00
Marcus Moore
fc4e8c68f2 docs: add @Scorcher as a contributor 2025-05-05 12:53:58 -07:00
Marcus Moore
6fb1c03908 docs: add @nticaric as a contributor 2025-05-05 12:53:57 -07:00
Marcus Moore
96ccfdb8cc docs: add @firefrei as a contributor 2025-05-05 12:53:57 -07:00
Marcus Moore
75fd07e057 docs: add @mzack5020 as a contributor 2025-05-05 12:53:56 -07:00
Marcus Moore
87fe69ecfb docs: add @Mateus-Romera as a contributor 2025-05-05 12:53:55 -07:00
Marcus Moore
19b47030ca docs: add @Nevets82 as a contributor 2025-05-05 12:53:54 -07:00
Marcus Moore
cf4e3fcc37 docs: add @snazy2000 as a contributor 2025-05-05 12:53:54 -07:00
Marcus Moore
bef4133f51 docs: add @smcpeck as a contributor 2025-05-05 12:53:53 -07:00
Marcus Moore
7ac24efced docs: add @cendai-mis as a contributor 2025-05-05 12:53:52 -07:00
Marcus Moore
d8d4a7075e docs: add @Shankschn as a contributor 2025-05-05 12:53:51 -07:00
Marcus Moore
dd4c9df6d1 docs: add @serkanerip as a contributor 2025-05-05 12:53:50 -07:00
Marcus Moore
da28c02b50 docs: add @SBrown2021 as a contributor 2025-05-05 12:53:50 -07:00
Marcus Moore
54858402e3 docs: add @McG800 as a contributor 2025-05-05 12:53:49 -07:00
Marcus Moore
b39d8cc0b9 docs: add @rosscdh as a contributor 2025-05-05 12:53:48 -07:00
Marcus Moore
4c7c33800a docs: add @rickheil as a contributor 2025-05-05 12:53:47 -07:00
Marcus Moore
d2c604a7ce docs: add @Nothing4You as a contributor 2025-05-05 12:53:47 -07:00
Marcus Moore
91243fb6c0 docs: add @mbrrg as a contributor 2025-05-05 12:53:46 -07:00
Marcus Moore
940a85888a docs: add @deloz as a contributor 2025-05-05 12:53:45 -07:00
Marcus Moore
280c12e22b docs: add @Galaxy102 as a contributor 2025-05-05 12:53:44 -07:00
Marcus Moore
39e644d048 docs: add @manu-crealytics as a contributor 2025-05-05 12:53:44 -07:00
Marcus Moore
9acb3e5935 docs: add @marcquark as a contributor 2025-05-05 12:53:43 -07:00
Marcus Moore
8ac5b5df61 docs: add @brandon-bailey as a contributor 2025-05-05 12:53:42 -07:00
Marcus Moore
434932599c docs: add @thinkl33t as a contributor 2025-05-05 12:53:42 -07:00
Marcus Moore
81b8c445c6 docs: add @vicleos as a contributor 2025-05-05 12:53:41 -07:00
Marcus Moore
002bb72a8d docs: add @herroworrd as a contributor 2025-05-05 12:53:40 -07:00
Marcus Moore
288770900e docs: add @robintemme as a contributor 2025-05-05 12:53:39 -07:00
Marcus Moore
f6f6a23f8b docs: add @wewhite as a contributor 2025-05-05 12:53:39 -07:00
Marcus Moore
522fa7be44 docs: add @Serdnad as a contributor 2025-05-05 12:53:38 -07:00
Marcus Moore
272d9e0552 docs: add @mink-adao-duy as a contributor 2025-05-05 12:53:37 -07:00
Marcus Moore
9060a3cc13 docs: add @ahpaleus as a contributor 2025-05-05 12:53:36 -07:00
Marcus Moore
b6a9c0e68b docs: add @DanielRuf as a contributor 2025-05-05 12:53:36 -07:00
Marcus Moore
b43ae5be13 docs: add @dkaatz as a contributor 2025-05-05 12:53:35 -07:00
Marcus Moore
6384041107 docs: add @seanborg-codethink as a contributor 2025-05-05 12:53:34 -07:00
Marcus Moore
89703cd9df docs: add @sorvani as a contributor 2025-05-05 12:53:33 -07:00
Marcus Moore
f6aa9f1318 docs: add @fe80 as a contributor 2025-05-05 12:53:33 -07:00
Marcus Moore
838e214b24 docs: add @phil-flip as a contributor 2025-05-05 12:53:32 -07:00
Marcus Moore
628c444cd4 docs: add @aranar-pro as a contributor 2025-05-05 12:53:31 -07:00
Marcus Moore
e8289b0f45 docs: add @JonathonReinhart as a contributor 2025-05-05 12:53:31 -07:00
Marcus Moore
86816f632f docs: add @test1337ahp as a contributor 2025-05-05 12:53:30 -07:00
Marcus Moore
494710306b docs: add @NebelKreis as a contributor 2025-05-05 12:53:29 -07:00
Marcus Moore
7a5fe4981f docs: add @mnemonicly as a contributor 2025-05-05 12:53:29 -07:00
snipe
a67b320cae Merge pull request #15907 from uberbrady/protect_assigned_to_assigned_type_rebased
Rebased version of #15629 - prevent setting assigned_to without setting assigned_type
2025-05-05 20:42:46 +01:00
Godfrey M
e3a2397b74 removed hiding the notifications icon 2025-05-05 10:28:08 -07:00
Godfrey M
3b34654dd7 Merge branch 'develop' into MS_teams_deprecation_update
# Conflicts:
#	resources/lang/en-US/admin/settings/message.php
2025-05-05 09:33:32 -07:00
Godfrey M
4b6437854c swapped out hard coded text with translation 2025-05-05 09:31:23 -07:00
snipe
9ef20997a5 Merge pull request #16861 from uberbrady/cherry_pick_avery_3490
adds support for Avery 3490 (Cherry-picked from 'master')
2025-05-05 15:01:00 +01:00
Calvin Schwartz
cfd10ae294 adds support for Avery 3490 2025-05-05 14:59:10 +01:00
snipe
9b85e9a071 Add @realchrisolin as a contributor 2025-05-05 14:44:04 +01:00
snipe
407962d998 Merge pull request #16790 from Godmartinz/empty_field_columns_labels
Adds Label fields offset as an option
2025-05-05 14:09:40 +01:00
snipe
1245289906 Add @chfsx as a contributor 2025-05-05 14:08:31 +01:00
snipe
76c19202ed Merge pull request #16806 from chfsx/patch-1
[FIX] set upload-limit
2025-05-05 14:07:42 +01:00
snipe
ada8195e2e Merge pull request #16860 from grokability/better_location_scope_check
Livewire component for smoother check for location companies
2025-05-05 14:04:23 +01:00
snipe
83d6e9ad8a Removed log error
Signed-off-by: snipe <snipe@snipe.net>
2025-05-05 14:00:05 +01:00
snipe
4469db0bd3 Livewire component for smoother check for location companies
Signed-off-by: snipe <snipe@snipe.net>
2025-05-05 13:55:28 +01:00
snipe
5e2dba5483 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-05-05 11:15:46 +01:00
Fabian Schmid
9de97694c3 [FIX] set upload-limit 2025-05-05 08:35:45 +02:00
Jeremy Price
8349065b0a stop buiulding intel on arm 2025-05-02 10:59:42 +02:00
Jeremy Price
bb82b2513e Fix dockerhub repo we're pushing to, and arm build names 2025-05-02 10:58:25 +02:00
snipe
e781c170f3 Merge pull request #16841 from Godmartinz/dark_mode_black_input_group_add_on_color_fix
remove duplicate input-group color corrections from theme skins
2025-05-01 01:48:49 +01:00
Godfrey M
43dfbd3d21 remove duplicate input-group color correction from other themes 2025-04-30 16:21:15 -07:00
Godfrey M
073c9f5f7c remove duplicate input-group color correction 2025-04-30 16:16:14 -07:00
snipe
f9d67dd431 Merge pull request #16840 from uberbrady/docker_backmerge
Copy changes from 'master' to develop for docker builds
2025-04-30 21:19:22 +01:00
Brady Wetherington
a2ea4c7fd0 Copy changes from 'master' to develop for docker builds 2025-04-30 21:12:10 +01:00
snipe
a0358e32d7 Removed escaping
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 16:46:06 +01:00
snipe
c2023c5c56 Ampersand showing in Custom fields [sc-29051]
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 16:25:44 +01:00
snipe
43c310c82d Merge pull request #16531 from akemidx/bug/sc-28715
FIXED: Purchase Cost Column Always Shown
2025-04-30 15:54:31 +01:00
snipe
939a0c44dc Merge pull request #16826 from Godmartinz/fix_multiple_inline_label_field_options
Reworked fix for for 24mm_D label indent errror
2025-04-30 15:47:57 +01:00
snipe
2b9cf1663b Merge pull request #16837 from grokability/fmcs_scoping_improvements
Small improvements to scoped views
2025-04-30 15:33:53 +01:00
snipe
0a29e90701 Smal improvements to scoping displays
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 15:24:42 +01:00
snipe
d1be13e7d4 Added button text to translations
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 14:32:29 +01:00
snipe
049a777ca8 Added buttons to dashboard
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 14:32:20 +01:00
snipe
0dcaa83a3e Nicer breadcrumb
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 14:22:13 +01:00
snipe
db706269e6 Fixed validation message
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 14:22:06 +01:00
snipe
4f72505dc3 Merge pull request #16836 from grokability/fixes/#16834_handle_bad_data_in_permissions
Fixed #16834 - better handle bad data in permissions
2025-04-30 13:56:59 +01:00
snipe
340f8b73a5 Updated comments
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 13:56:37 +01:00
snipe
6c6b37000a Better handle permissions with bad data
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 13:43:05 +01:00
snipe
b5c79624c6 Display notes in group listing
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 11:14:42 +01:00
snipe
49d66dedf4 Nicer existing image display
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 10:19:12 +01:00
snipe
ebbcdbc864 Nicer breadcrumb
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 10:11:55 +01:00
snipe
a18691c09f Set image path in singleton
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 10:11:48 +01:00
snipe
245b0b0f8f Added image path
Signed-off-by: snipe <snipe@snipe.net>
2025-04-30 10:11:40 +01:00
snipe
1d3b0478f9 Merge pull request #16829 from marcusmoore/fixes/contributors-fix
Fixed updating CONTRIBUTORS.md via cli
2025-04-30 01:23:59 +01:00
Marcus Moore
a5d0307532 Wrap username in quotes 2025-04-29 16:20:00 -07:00
snipe
7daecdd53f Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 23:17:03 +01:00
snipe
667b4a49c3 Merge pull request #16828 from grokability/chore/sc-29037
Add audit button to BS table partial, redirect if asset won't validate
2025-04-29 23:07:45 +01:00
snipe
2518e60a5e Corrected gates
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 22:59:05 +01:00
snipe
9ff8b62cee Updated icon
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 22:58:58 +01:00
snipe
5086c80658 Invoke a validator, redirect to edit screen if invalid
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 22:47:34 +01:00
snipe
cb852fc20f Added audit gate to API
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 22:47:12 +01:00
snipe
fb3b34e0f6 Add audit button to BS table partial
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 22:47:02 +01:00
snipe
8d4fc07f63 Updated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 20:37:47 +01:00
snipe
a0514ad8c1 Merge pull request #16799 from marcusmoore/bug/sc-28990
Handle category being null in CheckoutableListener
2025-04-29 20:37:11 +01:00
snipe
4e03e525a4 Merge pull request #16797 from Godmartinz/dark_mode_green_fix
Fixed Dark Mode color choices for fieldset links
2025-04-29 20:35:59 +01:00
snipe
0efdebcfd8 Merge pull request #16827 from grokability/fixed_custom_field_fieldset_display
Fixed fieldset display if custom fields are not available
2025-04-29 20:26:14 +01:00
snipe
c7835d2d1d Fixed copypasta
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 20:08:34 +01:00
snipe
3e3bc0a347 Removed test tab
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 20:01:22 +01:00
snipe
184a22828f Use input vs get
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 19:22:10 +01:00
snipe
f26e27d23e Display in checkboxes not saving on custom [sc-29028]
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 19:21:50 +01:00
snipe
e717f1e780 Removed logging
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 19:21:18 +01:00
snipe
d1085a0f46 Move notes above custom fields
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 18:53:31 +01:00
Godfrey M
2e0913bb3b remove unused method 2025-04-29 10:49:19 -07:00
Godfrey M
851ae46ea9 reworked fix for for 24mm_D label indent errior 2025-04-29 10:45:29 -07:00
snipe
89a52b7551 Fixed fieldset display if custom fields are not available
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 18:09:32 +01:00
snipe
15870d0e75 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 12:38:07 +01:00
snipe
eb2c536221 Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 12:36:55 +01:00
snipe
bced7df539 Merge pull request #16822 from grokability/nicer_optional_disclosures
Nicer disclosure UI for optional data
2025-04-29 12:31:14 +01:00
snipe
fe672ed727 Nicer disclosure UI for optional data
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 12:10:09 +01:00
snipe
6e60594e66 Fixed escaped text
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 10:25:20 +01:00
snipe
f357dd690b Updated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 10:21:24 +01:00
snipe
255e0c3bdc Merge pull request #16602 from akemidx/bug/sc-25897-2
FIXED: Text Overflow on Settings Tiles
2025-04-29 10:20:45 +01:00
snipe
8984d60c39 Merge pull request #16798 from grokability/more-mint-support-and-newer-ubuntus
Support more Mint versions and verify newer Ubuntu versions in snipeit.sh
2025-04-29 10:19:28 +01:00
snipe
6dbfc8875b Merge pull request #16817 from marcusmoore/fixes/restore-custom-field-values-on-asset-edit-v2
Maintain checkbox and radio custom field values on asset edit page
2025-04-29 10:17:10 +01:00
snipe
139f45872c Merge pull request #16819 from marcusmoore/chore/migrate-radio-helpers
Replaced calls to Form::radio helper on user create and edit pages
2025-04-29 10:16:16 +01:00
snipe
eb223a4c09 Merge pull request #16821 from grokability/small_settings_ui_fixes
Use default BS tables “no results” view, small UI formatting improvements
2025-04-29 10:15:16 +01:00
snipe
c3531e9eba Nicer formatting on smaller screens
Signed-off-by: snipe <snipe@snipe.net>
2025-04-29 10:11:00 +01:00
Marcus Moore
db3f8e5d68 Replace a Form::radio on user edit page 2025-04-28 15:08:05 -07:00
Marcus Moore
8ca11542f8 Replace a Form::radio on user edit page 2025-04-28 15:03:54 -07:00
Marcus Moore
36be23f7e4 Replace a Form::radio on user edit page 2025-04-28 14:57:03 -07:00
Marcus Moore
51f67082f4 Replace a Form::radio on user edit page 2025-04-28 14:23:39 -07:00
Marcus Moore
ea2f0cdd7b Replace a Form::radio on user edit page 2025-04-28 14:18:01 -07:00
Marcus Moore
97d2e6f9d4 Replace a Form::radio on user edit page 2025-04-28 14:03:27 -07:00
Marcus Moore
28ea75512c Replace from Form::radio on user edit page 2025-04-28 13:47:23 -07:00
snipe
9b6683ae16 Merge pull request #16818 from Godmartinz/fix_multiple_inline_label_field_options
Fixed label fields multiple option alignment bug
2025-04-28 21:37:19 +01:00
Godfrey M
149d276e06 fix field alignment issue 2025-04-28 13:26:49 -07:00
snipe
a519ebe19b Default BS table no results
Signed-off-by: snipe <snipe@snipe.net>
2025-04-28 09:59:52 +01:00
snipe
66b537bc64 Fixed duplicate ID
Signed-off-by: snipe <snipe@snipe.net>
2025-04-28 09:59:28 +01:00
snipe
9722d29070 Use default BS tables “no results” view
Signed-off-by: snipe <snipe@snipe.net>
2025-04-28 09:59:17 +01:00
snipe
c748278637 Merge pull request #16810 from grokability/settings_improvements_branding
Use fieldsets for branding page
2025-04-26 17:53:11 +01:00
snipe
23c39520e5 Added empty alt tags to images since we don’t have additional info
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:42:49 +01:00
snipe
41f68d8a30 Use locked x-icon
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:33:18 +01:00
snipe
7320e823ad Added alt text for sad panda
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:33:09 +01:00
snipe
1aeda546fd Added urls for pa11y
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:33:00 +01:00
snipe
91fcff5faf Added translations
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:32:52 +01:00
snipe
d97e54f85e Use fieldsets for branding page
Signed-off-by: snipe <snipe@snipe.net>
2025-04-26 17:32:41 +01:00
snipe
4b58af8850 Fixed message while processing audit on quickscan
Signed-off-by: snipe <snipe@snipe.net>
2025-04-24 14:25:42 +01:00
snipe
0822aa985d Fixed #16802 - use asset endpoint for assets assigned to locations
Signed-off-by: snipe <snipe@snipe.net>
2025-04-24 14:05:44 +01:00
snipe
e5c6e294ec Added #16239 - make city searchabke in users
Signed-off-by: snipe <snipe@snipe.net>
2025-04-24 12:16:41 +01:00
Marcus Moore
25fdde1807 Handle null category 2025-04-23 14:16:28 -07:00
snipe
fa45ca1453 Stupid fix for when people use id instead of an actual ID
@todo - use RMB for these

Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:56:43 +01:00
Jeremy Price
67ec042ee3 Support more Mint versions and verify newer Ubuntu versions in snipeit.sh
In https://github.com/grokability/snipe-it/pull/16763 we added support
for Linux Mint 22.1, which is the newest stable version of Mint, but but
didn't get around to testing or adding support for less-recent versions.

With this PR, we're adding support for the following LinuxMint versions
* 22
* 21.3
* 21.2
* 21.1
* 21

We recommend using the newest version, but at least now you won't be
blocked so long as you're remotely up-to-date.

We're not going back any further because prior versions are based on
Ubuntu 20.04 (Focal), which is out of support as of April 2025, aka this
month.

Additionally, the most recent Ubuntu version specifically mentioned in
the script is 23.10. The script in its current version works all the way
up to the most recent version, so we're adding notation to that
effect.

Newly included Ubuntu versions are
25.04
24.10
24.04 (LTS)
2025-04-23 13:37:33 -07:00
snipe
8b6c88a7c6 Okay…
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:27:28 +01:00
snipe
9f8fddb4c5 Removed accidentally commited generated images
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:23:58 +01:00
snipe
8fcf7e3b9d Merge pull request #16792 from grokability/add_pa11y
Added pa11y
2025-04-23 21:17:35 +01:00
snipe
d9326fc555 Removed sky-is-blue text
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:13:40 +01:00
snipe
c1196599e1 Merge branch 'develop' into add_pa11y 2025-04-23 21:11:59 +01:00
Godfrey M
a26279e0b9 adds custom field link color correction to all dark modes 2025-04-23 12:38:58 -07:00
Marcus Moore
29433882ea Restore custom field checkbox and radio button values when switching model 2025-04-23 12:38:09 -07:00
snipe
e3f511ec7c Small accessibility improvements
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 15:53:41 +01:00
snipe
f0b18042f9 Added aria label to admin settings search
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 14:01:52 +01:00
snipe
75c9936dbb Added alt text
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 12:37:13 +01:00
snipe
ddd4065c81 Merge pull request #16793 from spencerrlongg/bug/sc-28972
Added Nullsafe Checks to Users
2025-04-23 12:01:25 +01:00
snipe
ad88a72d0a Override legend style
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 06:31:25 +01:00
snipe
6a00620552 More urls
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 06:27:20 +01:00
snipe
a9db8d6898 Added more urls
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 06:20:23 +01:00
snipe
65cc1bbd7e Ignore screenshots
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 05:53:11 +01:00
spencerrlongg
2935697209 added nullsafe checks to user 2025-04-22 23:39:24 -05:00
snipe
3ae2454228 This doesn’t work the way I thought
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 05:35:49 +01:00
snipe
11b48ee636 Removed dupes
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 05:21:32 +01:00
snipe
2aa864afaa Updated rules
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 05:16:22 +01:00
snipe
ec65e64a67 Updated ignore rules
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 05:06:34 +01:00
snipe
7e01d23aa2 Renamed file
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 04:50:14 +01:00
snipe
f05b2ad9be First stabs at improvements
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 04:48:47 +01:00
snipe
eaaac76435 Added configs
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 04:48:39 +01:00
snipe
41160c64a8 Merge pull request #16791 from grokability/location_company_scoping_improvements
Improved settings page for location-company scoping
2025-04-23 01:25:54 +01:00
snipe
0cfef59568 Updated string
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 01:22:25 +01:00
snipe
d953d1a889 Use translation strings
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 01:21:54 +01:00
snipe
4aa06f6a75 Update dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 00:30:06 +01:00
snipe
07a9bded95 Added strings
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 23:50:28 +01:00
snipe
f686e86afb Refactored setting page
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 23:50:21 +01:00
snipe
9f28dae01b Invoke the artisan command
This *might* suck for people with a lot of assets, etc

Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 23:50:11 +01:00
snipe
67ab584dc7 Updated text
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 23:49:31 +01:00
Godfrey M
5da492cbf5 set max to 5 2025-04-22 12:07:02 -07:00
Godfrey M
d871c529d1 fix input max, and help block position 2025-04-22 11:59:19 -07:00
Godfrey M
7c2c5ea98d adds Field offset option to labels 2025-04-22 10:50:20 -07:00
snipe
2e31a0530f Merge pull request #16789 from grokability/fixes/#16475_delete_oauth_clients
Fixed #16475 - Allow deleting oauth client
2025-04-22 17:48:24 +01:00
snipe
5f66fb0bba Allow deleting oauth user
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 17:42:07 +01:00
snipe
143e9cdd61 Merge pull request #16788 from grokability/better_handle_arrays_for_model_ids
Better handle model_id arrays passed to the API
2025-04-22 16:33:47 +01:00
snipe
aed32e6ada Better handle arrays in API rerquests for model_id
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 16:23:34 +01:00
snipe
ed86c90b7e Merge pull request #16787 from uberbrady/suppress_action_date_error_in_loggable
Fixed: [RB-19645] Suppress error message about 'action_date' not existing
2025-04-22 14:48:38 +01:00
snipe
7dc606fd3b Merge pull request #16786 from grokability/populate_manufacturers
Added ability to seed common manufacturers
2025-04-22 14:35:20 +01:00
snipe
185fd559c9 Added test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 14:34:03 +01:00
snipe
473c684fa5 Nicer translation
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 14:22:58 +01:00
Brady Wetherington
d3dbd82ce2 Check that array key exists before accessing it 2025-04-22 14:20:01 +01:00
snipe
e5dc13e48c Added ability to seed manufacturers
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 14:13:46 +01:00
snipe
2a2d118973 Updated readme with star count
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 12:38:05 +01:00
snipe
f2f17402c9 Merge pull request #16785 from grokability/feature/sc-28956
Added checkout date to license seats
2025-04-22 11:41:52 +01:00
snipe
39f764803d Added checkout date to license seats
Signed-off-by: snipe <snipe@snipe.net>
2025-04-22 11:38:54 +01:00
snipe
55176816aa Merge pull request #16784 from marcusmoore/fix-flaky-timestamp-tests
Fixed flaky test
2025-04-22 11:03:58 +01:00
Marcus Moore
1f7d4e0793 Fix flaky test 2025-04-21 15:14:53 -07:00
snipe
e17fae02ad Merge pull request #16783 from uberbrady/upgrade_debugbar
Upgrade Debugbar to make deprecation warnings easier to find
2025-04-21 21:44:28 +01:00
Brady Wetherington
3c1099a6a9 Upgrade Debugbar to make deprecation warnings easier to find 2025-04-21 21:00:02 +01:00
snipe
b1761ec246 Merge pull request #16781 from uberbrady/make_min_qty_not_required
min_amt wasn't correctly being set to required or not
2025-04-21 18:44:20 +01:00
Brady Wetherington
03f0f13727 min_amt wasn't correctly being set to required or not 2025-04-21 18:32:53 +01:00
snipe
12648912aa Added test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 16:45:10 +01:00
snipe
4c898a8741 Fixed filename
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 16:44:47 +01:00
snipe
6bef8620e4 Merge pull request #16780 from grokability/feature/sc-23604
Fixed #9249 - added file uploads to locations
2025-04-21 14:49:02 +01:00
snipe
beb5560dce Added files tab
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 14:43:04 +01:00
snipe
2ebe1ebc69 Load location route files
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 14:42:58 +01:00
snipe
00092a079f Added uploads method
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 14:42:10 +01:00
snipe
9d313eb2d9 Added locations dir
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 14:42:02 +01:00
snipe
e8404c8720 Moved location routes
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 14:41:52 +01:00
snipe
e71e25955a Check for array
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 12:20:03 +01:00
snipe
fa9ac3c449 Return value as int
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 11:53:26 +01:00
snipe
70854b2c42 Small fix or groups API
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 11:49:49 +01:00
snipe
e2a1be9762 Int values for group permissions on API
Signed-off-by: snipe <snipe@snipe.net>
2025-04-21 10:49:28 +01:00
snipe
f2c2fefd99 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 15:28:48 +01:00
snipe
b5a960e933 Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 15:27:41 +01:00
snipe
66b2cc2e28 Set empty array if permissions are null
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 15:17:55 +01:00
snipe
47246a3fdf Merge pull request #15420 from grokability/return_boolean_in_api
Possible fix for #15315 - decode as permissions as boolean
2025-04-19 15:01:36 +01:00
snipe
4d01b2bb4f Removed invalid json_decode() flag
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 14:57:43 +01:00
snipe
876715b3c5 Merge pull request #16435 from marcusmoore/fixes/prevent-deleting-accessories-with-checkouts
Disallow deleting accessories that have active checkouts
2025-04-19 14:38:14 +01:00
snipe
93d74587e1 Merge pull request #16538 from marcusmoore/bug/sc-28305
Return null from accessory transformer for missing assignment
2025-04-19 14:37:45 +01:00
snipe
5bae74bc1b Merge pull request #16688 from 36864/patch-1
Fixes #14734: Only show signatures for the printed user
2025-04-19 14:36:26 +01:00
snipe
0259c91a06 Added @mrdahbi as a contributor
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 14:34:15 +01:00
snipe
04ccfc3002 Fixed: escape '/' in preg_quote of asset tag prefix (Applied #16542 to develop)
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 14:32:21 +01:00
snipe
495b7db72b Merge branch 'chore/sc-28495' into develop
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 14:29:30 +01:00
snipe
6e5eb55b45 Updated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 14:28:01 +01:00
snipe
9ad99c1d81 Merge pull request #16494 from marcusmoore/bug/sc-28675
Avoid logging consumable checkins and purge action log of bad entries
2025-04-19 14:24:52 +01:00
snipe
622626bb27 Merge pull request #16521 from grokability/add-disable-saml-command
Add console command to disable SAML logins
2025-04-19 14:23:49 +01:00
snipe
f66575393a Merge pull request #16766 from CloCkWeRX/fix-placeholder-translations
Fixed: Admin > General Settings - Some placeholders not translatable
2025-04-18 16:40:53 +01:00
snipe
f67548cd70 Merge pull request #16738 from CloCkWeRX/patch-9
Fixed: Update SECURITY.md to indicate v8 is supported
2025-04-18 16:33:18 +01:00
snipe
cd63657a92 Merge pull request #16694 from r-xyz/api-files-notes
Fixed #16689: re-add `note` field in API files listing for AssetModel
2025-04-18 14:06:30 +01:00
snipe
54f2b62294 Fixed consumable item_no import field
Signed-off-by: snipe <snipe@snipe.net>
2025-04-18 00:55:56 +01:00
snipe
1a1120220c Merge pull request #16765 from grokability/#15035-add-company_name_to_slack
Fixed #15035 - adds company to slack message
2025-04-18 00:36:25 +01:00
Daniel O'Connor
82d93b6980 Point to existing translation 2025-04-17 23:32:11 +00:00
Daniel O'Connor
72b2b2d819 Refactor to translatable placeholder text 2025-04-17 23:30:08 +00:00
snipe
ad6352adc4 Fixed #15035 - adds company to slack message
Signed-off-by: snipe <snipe@snipe.net>
2025-04-18 00:28:37 +01:00
snipe
4170397094 Merge pull request #16731 from CloCkWeRX/patch-4
Fixed: Editing > Email input > Utilise HTML5 controls
2025-04-17 23:16:23 +01:00
snipe
1e15aca809 Merge pull request #16733 from CloCkWeRX/patch-6
Fixed: Suppliers > Edit > Mark URL inputs as HTML5 URL inputs
2025-04-17 23:15:53 +01:00
snipe
8234c3eb0f Merge pull request #16734 from CloCkWeRX/patch-7
Fixed: Users > Edit > Mark website as a URL field
2025-04-17 23:14:59 +01:00
snipe
80eea7b064 Merge pull request #16757 from CloCkWeRX/fields
Fixed: Admin > Custom Fields > Ensure name field is marked required
2025-04-17 23:14:26 +01:00
snipe
779c28661e Merge pull request #16753 from CloCkWeRX/admin-slack-hook
Fixed: Admin > Webhooks > Swap to url input
2025-04-17 23:13:58 +01:00
snipe
516f59f0fc Added @BeatSpark as contributor
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 23:12:51 +01:00
snipe
bfb55da1a3 Repeat #16571 without conflicts, correctly targeted
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 23:10:20 +01:00
snipe
79e2e5c272 Merge pull request #16729 from CloCkWeRX/patch-2
Fixed #16727: Bulk Audit > Mark Asset Tag input required
2025-04-17 23:06:26 +01:00
snipe
db1af98992 Merge pull request #16764 from grokability/fixed_flaky_user_create_tests
Fixed flaky user creation tests
2025-04-17 22:46:42 +01:00
snipe
c8ea3ba79a Merge pull request #16763 from grokability/add-linux-mint
Fixed: #16715 - Added LinuxMint support to snipeit.sh
2025-04-17 22:40:33 +01:00
snipe
96d5e072fe Fixed flaky tests on user creation
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 22:39:04 +01:00
snipe
d25ba74123 Merge pull request #16762 from grokability/fixes/#15702-min_amt_and_termination_date_on_license_import
Fixed #15702 - Added termination_date, reordered fields for clarity
2025-04-17 21:37:27 +01:00
snipe
f05dce8960 Added min_amt explicitly
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 21:31:20 +01:00
snipe
0758e73c5f Added termination_date, reordered fields for clarity
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 21:29:27 +01:00
Jeremy Price
617ee026c0 Add LinuxMint support to snipeit.sh
Fixes: https://github.com/grokability/snipe-it/issues/16715

@synergy34 requested LinuxMint support in our installer script. Mint is
Ubuntu-based, so i figured this wouldn't be too big of an issue, and
indeed it wasn't.

It mostly involved just supporting the OS version strings, and removing
the ondrej ppa because 22.1 ships with php8.3 already.

We only support 22.1 for the moment, as that's the most recent version
and the only one I tested on.

If you're on 22/Wilma and want to try, change the 221 on line 579 to 22 and
give it a whirl... If 22/Wilma also has php8.3, then it will probably be
fine
2025-04-17 12:22:42 -07:00
Daniel O'Connor
0cec64c056 Update SECURITY.md to indicate v8 is support
Unsure if v7 still is
2025-04-17 16:02:31 +00:00
snipe
915c730dae Fixed #16714 - moved custom validation out of array
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 16:42:25 +01:00
snipe
0451f1219a Merge pull request #16732 from CloCkWeRX/patch-5
Fixed: Manufacturers > Edit > Mark URL inputs as HTML5 url inputs
2025-04-17 16:27:03 +01:00
Daniel O'Connor
5290a95b6b Update resources/views/hardware/quickscan.blade.php 2025-04-18 00:53:37 +09:30
snipe
e407695ff9 Merge pull request #16741 from CloCkWeRX/patch-10
Fixed: Change Password > Mark password fields required for change password
2025-04-17 16:22:01 +01:00
snipe
c5b53b00c1 Merge pull request #16742 from CloCkWeRX/security-url
Fixed: Admin > Security - Mark url fields as URL type
2025-04-17 16:21:26 +01:00
snipe
d2ac9b9610 Added @CloCkWeRX as contributor
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 16:18:29 +01:00
snipe
537e695ae9 Merge pull request #16730 from CloCkWeRX/patch-3
Fixed: Depreciations > Create/Edit > Change controls to various number inputs
2025-04-17 16:15:24 +01:00
snipe
b4b158da9e Merge pull request #16747 from CloCkWeRX/admin-oauth-required
Fixed: Admin > OAuth > Mark fields required
2025-04-17 16:09:42 +01:00
snipe
ad072c0546 Moved validation email_array
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 16:04:56 +01:00
snipe
713e1671d4 Merge pull request #16749 from CloCkWeRX/admin-ldap-change-password-url
Fixed: Admin > LDAP > Use HTML5 inputs
2025-04-17 15:58:45 +01:00
snipe
087f9756a2 Merge pull request #16744 from CloCkWeRX/api-token-required
Fixed: Manage API Keys > New name field not marked required
2025-04-17 15:58:07 +01:00
snipe
eb0408703e Merge pull request #16725 from CloCkWeRX/patch-1
Fixed #16723: Mark category name as required in modals
2025-04-17 15:47:07 +01:00
Daniel O'Connor
7e961b690a Mark required 2025-04-17 11:05:03 +00:00
snipe
81f3730d84 Merge pull request #16754 from grokability/fixes/ambiguous_clause_for_company_id
Fixed ambiguous clause using company_id
2025-04-17 11:58:15 +01:00
snipe
141fad8393 Fixed ambiguous clause using company_id
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 11:56:39 +01:00
Daniel O'Connor
58ff63845e Admin > Slack > Swap to url input 2025-04-17 10:47:37 +00:00
Daniel O'Connor
a8150b7864 Update resources/views/settings/ldap.blade.php 2025-04-17 19:57:52 +09:30
Daniel O'Connor
ce6724f788 Admin > LDAP > Swap to URL input 2025-04-17 10:25:23 +00:00
Daniel O'Connor
13faa8ab00 Admin > LDAP > Swap to URL input 2025-04-17 10:23:43 +00:00
Daniel O'Connor
89beb73836 Swap to URL input 2025-04-17 19:49:28 +09:30
Daniel O'Connor
8b7e36a697 Mark fields required 2025-04-17 10:17:32 +00:00
Daniel O'Connor
c0f3d89b0b Fixed: Manage API Keys > New name field not marked required 2025-04-17 10:00:26 +00:00
Daniel O'Connor
ec62a59e57 Update resources/views/depreciations/edit.blade.php 2025-04-17 19:20:44 +09:30
Daniel O'Connor
f9269cfc63 Mark required 2025-04-17 19:19:19 +09:30
Daniel O'Connor
56bc06746c Set max limit 2025-04-17 19:18:31 +09:30
Daniel O'Connor
497e94d8a0 Admin > Security - Mark url fields as URL type 2025-04-17 09:42:45 +00:00
Daniel O'Connor
aa77c8c528 Mark password fields required for change password 2025-04-17 09:40:27 +00:00
Daniel O'Connor
6cd2a5d1a5 Users > Edit > Mark website as a URL field 2025-04-17 09:35:35 +00:00
Daniel O'Connor
e4a3a1a35f Suppliers > Edit > Mark URL inputs as HTML5 URL inputs 2025-04-17 09:34:45 +00:00
Daniel O'Connor
06f3cc1345 Manufacturers > Edit > Opt into HTML fields 2025-04-17 09:33:24 +00:00
Daniel O'Connor
3a6832ea58 Editing > Email input > Utilise HTML5 controls
Should be safe as this is validated by the UI as a singular email.
2025-04-17 09:32:02 +00:00
Daniel O'Connor
d2bb7fc926 Deprecations > Create/Edit > Mark number of months as a numeric control 2025-04-17 09:30:56 +00:00
Daniel O'Connor
4b93f329c2 Bulk Audit > Mark Asset Tag input required
Fix https://github.com/grokability/snipe-it/issues/16727
2025-04-17 09:29:43 +00:00
Daniel O'Connor
f03da92152 Mark category name as required in modals
Fix #16723
2025-04-17 09:27:04 +00:00
snipe
88acdbcc28 Move array return
Signed-off-by: snipe <snipe@snipe.net>
2025-04-17 01:00:09 +01:00
snipe
23623cca2c Merge pull request #16719 from grokability/fixes_weird_layout_datepicker
Use x-icon blade component, nicer small-screen form size for datepicker on assets checkout
2025-04-16 21:13:21 +01:00
snipe
93e66aae54 Nicer form sizing on smaller screens
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 21:05:56 +01:00
snipe
02b831c174 Use x-icon blade component
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 21:05:43 +01:00
snipe
cd20486fe2 Merge pull request #16667 from Godmartinz/user-redirect-checkin-option
Adds option to redirect back to checkedInBy user for assets/licenses/accessories
2025-04-16 20:54:47 +01:00
snipe
1464f80425 Merge pull request #16717 from Godmartinz/fix_checkin_mail_test
Adds a check for category email alert boolean, bolster Check in Test
2025-04-16 18:10:29 +01:00
Godfrey M
8f673a7e3e default checkin_email to true in category factory 2025-04-16 10:05:37 -07:00
Godfrey M
c44d037933 removed some words 2025-04-16 09:57:40 -07:00
Godfrey M
c8b5b3f176 adds a check for category checkin/out emails, also updates our test 2025-04-16 09:51:44 -07:00
snipe
ce94470a10 Added comments
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 16:41:25 +01:00
snipe
e7592eeeb9 Use disable admin CC
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 16:41:14 +01:00
snipe
46253b421e Added disableAdminCC to settings test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 16:41:04 +01:00
snipe
7d4e77a7c8 Updated string
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 15:52:05 +01:00
snipe
9a4f21e0cb Merge pull request #16716 from grokability/log_deprecations
Allow toggle for logging deprecation warnings
2025-04-16 14:28:07 +01:00
snipe
934aa3da7f Allow toggle for logging deprecation warnings
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 14:25:51 +01:00
snipe
e4244d60f1 Check for username
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 09:47:06 +01:00
snipe
0b6c6bf1df Send email to CC addresses even if the target doesn’t have an email
Signed-off-by: snipe <snipe@snipe.net>
2025-04-16 09:18:50 +01:00
snipe
b35181c289 Removed deleted accessories (for now) from transformer
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 21:15:43 +01:00
snipe
277564436b Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:45:13 +01:00
snipe
87a03ec1ed Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:41:43 +01:00
snipe
f8833241ef Added @36864 as contributor
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:28:36 +01:00
snipe
7f62c5cbb6 Add @fvollmer as a contributor 2025-04-15 20:22:23 +01:00
snipe
93b4749993 Update @ntaylor-86 as a contributor 2025-04-15 20:20:36 +01:00
snipe
b2dac291da Merge pull request #16712 from grokability/fixes/snipe-api-token
Removed deprecation resulting in `Creation of dynamic property` error
2025-04-15 20:16:56 +01:00
Jermops
bec83d4343 Merge pull request #16713 from grokability/fix-docker-arm-buildname
Fix name of ARM docker container workflow
2025-04-15 12:16:40 -07:00
snipe
4f3b3721c4 Remove comments
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:15:44 +01:00
Jeremy Price
e5cf296b79 Fix name of ARM docker container workflow 2025-04-15 12:15:44 -07:00
snipe
e1abdd1c7b Removed deprecation resulting in Creation of dynamic property
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:11:26 +01:00
snipe
71d8f1eb89 Merge pull request #16701 from grokability/repo-move
Update references to the repo to reflect move to Grokability org
2025-04-15 19:54:08 +01:00
snipe
68c1568345 Merge pull request #16711 from snipe/publish_assets
Updated dev CSS/LESS/JS assets
2025-04-15 19:40:48 +01:00
snipe
b5be0844ec Updated dev assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 19:39:58 +01:00
snipe
f76e80ba68 Merge pull request #16710 from snipe/localizations/2025-04-15
Updated localization strings
2025-04-15 19:32:12 +01:00
snipe
ffbab554be Updated strings
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 19:30:19 +01:00
snipe
0be50e803e Merge pull request #16709 from snipe/#16699-fix-email-locales-when-none-set-on-user
Fixed #16699 - Better handle user locales in mailables
2025-04-15 16:44:38 +01:00
snipe
7133a1b262 Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:41:09 +01:00
snipe
5876418eed Search on email partial
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:31:03 +01:00
snipe
950472b935 Fixed typos
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:30:54 +01:00
snipe
c0c5699e38 Added public property name
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:21:02 +01:00
snipe
49fee3a211 Removed manually setting locale on mailable
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:10:54 +01:00
snipe
a21ca92c90 Added boot method to set name property for mailable
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 16:08:21 +01:00
snipe
260174dfd9 Added test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 15:46:12 +01:00
snipe
afc5e08716 Added email as searchable field in select list
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 15:45:25 +01:00
snipe
89616727a1 Remove english as default
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 15:45:11 +01:00
snipe
ba55dfb841 Fixed #16699 - added mutator for user locale
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 13:41:24 +01:00
snipe
65b956143c Merge pull request #16700 from snipe/docker-arm-size-fix
[Docker] Don't cache composer deps, remove any .git repos that creep in
2025-04-14 22:15:10 +01:00
snipe
42e1987147 Merge pull request #16702 from snipe/separate-docker-architecture-builds
Separate docker builds into Intel/ARM builds
2025-04-14 22:14:48 +01:00
Jeremy Price
545e07455b Separate docker builds into Intel/ARM builds
Now that we're moving to a paid org, we have native ARM github action
runners available, which means we can streamline our ARM-based docker
image builds by not having them run emulated.

I've switched our worker split from ubuntu/alpine to Intel/ARM...  if we
hate this we can make it 4 separate workflows, but i don't see an issue
with this one.
2025-04-14 12:55:36 -07:00
Jeremy Price
b00594052c [Docker] Don't cache composer deps, remove any .git repos that creep in
There is a failure mode in composer where if it has a connection trying to get
a dependency tarball from the github API, it will fall-back to
downloading the entire repo for the dependency and use that... and it
will cache it outside of vendor/, using a whopping 1.5G of space

that full 1.5G is _then_ copied into vendor/

```
98M     league
100M	tecnickcom
133M	laravel
323M	sebastian
681M	aws
```

for a total of a 1.8G vendor/ directory

vs the trimmed-down version with a 552M vendor/ directory

```
53M	league
30M	tecnickcom
31M	laravel
70M	sebastian
241M	aws
```

This is still a far cry from the proper 150M version when everything
works as it should, but it's still a vast improvement

```
3.2M    league
14M     laravel
30M     tecnickcom
260K	sebastian
52M     aws
```

Ideally this never happens, but it'd be great tp avoid the bloat if/when
it does..

To wit: Our ARM/Ubuntu Docker images are currently bloated because of this
issue due builds happening on an emulated ARM environment, and the resultant
performance penalties causing composer issues

All that to say, this change sets OMPOSER_CACHE_DIR to /dev/null to
avoid the caching, and then removes vendor/*/*/.git to remove that
needless bloat if it occurs. It's a no-op in general practice.
2025-04-14 11:55:20 -07:00
snipe
1c387795fe Merge pull request #16683 from marcusmoore/bug/sc-28755
Create default label when importing assets if none exists
2025-04-14 09:52:48 +01:00
snipe
102f26cac1 Merge pull request #16676 from marcusmoore/fixes/acceptance-logging
Store accepted_at and declined_at in action log when accepting/declining assets
2025-04-14 09:18:54 +01:00
snipe
c7e89ff879 Merge pull request #16682 from akemidx/bug/sc-28860
FIXED: Location Being Overwritten By Default Location
2025-04-14 09:17:52 +01:00
r-xyz
565b8f5c7f Fixed #16689: re-add note field in API files listing for AssetModel 2025-04-13 00:33:22 +02:00
36864
3b314086f9 New method to get latest signature event
This makes the print page much tidier.
2025-04-11 16:28:54 +00:00
36864
06fc140626 Optimize eager loading of log entries 2025-04-11 16:27:29 +00:00
36864
e4bfc6c5ae Fix signatures for accessories and consumables
Also unifies the way these things are fetched.
2025-04-11 16:24:06 +00:00
Marcus Moore
b82d835f4f Remove created_by since it is not available 2025-04-10 13:59:28 -07:00
Marcus Moore
d593365c9c Create pending default status label if none exists 2025-04-10 13:38:30 -07:00
Marcus Moore
ea6a903d8a Implement tests including test failure 2025-04-10 13:29:05 -07:00
Marcus Moore
9086e5dba7 Scaffold some asset importer tests 2025-04-10 13:03:50 -07:00
akemidx
cd10cd34f4 location update bug fix and test 2025-04-10 14:46:52 -04:00
snipe
0d6a83197a Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2025-04-09 21:29:31 +01:00
snipe
6d784e36d7 Updated language
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 21:29:27 +01:00
snipe
9c88aa6974 Merge pull request #16637 from akemidx/lastnameemail
Fixed #8188 - Added Last Name as an email format
2025-04-09 21:27:10 +01:00
akemidx
1307146831 changing where translations live. coalescing 2025-04-09 16:21:48 -04:00
Godfrey M
c385b4a082 remove testing lines 2025-04-09 12:11:46 -07:00
Godfrey M
1b961346f0 added withInput to the redirects 2025-04-09 12:11:31 -07:00
Godfrey M
100db23210 add checks that the target is not null, and redirects back with error messages 2025-04-09 11:54:59 -07:00
Marcus Moore
e5d7bcb629 Use declinded at for action date in log 2025-04-09 11:20:46 -07:00
Marcus Moore
c2123e307a Add failing test 2025-04-09 11:20:07 -07:00
Marcus Moore
7a9d5bfc07 Add test for declining assets 2025-04-09 11:18:52 -07:00
Marcus Moore
aed798800c Improve assertions 2025-04-09 11:17:57 -07:00
Marcus Moore
dbfa952a69 Improve test name 2025-04-09 11:15:25 -07:00
Marcus Moore
aa58f08b3d Use accepted at for action date in log 2025-04-09 11:13:46 -07:00
Godfrey M
3e980a4c57 set location if target is set 2025-04-09 11:11:59 -07:00
Marcus Moore
d76871760c Add failing test 2025-04-09 11:11:30 -07:00
Marcus Moore
d29f5fa13e Implement tests 2025-04-09 11:07:47 -07:00
Marcus Moore
7275299165 Scaffold some tests 2025-04-09 11:02:56 -07:00
Godfrey M
9a3ac41370 add default location as a fallback to asset validation 2025-04-09 11:02:45 -07:00
Godfrey M
9824f43780 added other target options to accessories and assets 2025-04-09 10:50:42 -07:00
snipe
1b7486c342 Merge pull request #16674 from spencerrlongg/bug/allow-avif-restore-and-logo
Add Support for Uploading AVIF Logo Images
2025-04-09 17:46:53 +01:00
spencerrlongg
cd9ee8af90 Add support for AVIF logo uploads and restoring avifs from public uploads
Extended the list of allowed file extensions and MIME types to include AVIF format in both backend file validation and the file input field for logo uploads. This update ensures compatibility with modern image formats.
2025-04-09 11:36:16 -05:00
snipe
cbf4fef45b Merge pull request #16671 from snipe/fixed_list_view_of_asset_files
Partial fix for #16135 - normalized asset file listing at API endpoint
2025-04-09 06:45:32 +01:00
snipe
8892a11e7e Updated tests
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 06:40:14 +01:00
snipe
fc390dd107 Merge pull request #16672 from snipe/added_missing_gate_to_some_location_methods
Added gates to printing locations
2025-04-09 06:28:38 +01:00
snipe
99dfb51d70 Fixed test
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 06:23:40 +01:00
snipe
04f8ebb4d8 Added tests
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 06:17:08 +01:00
snipe
41fb058adb Added gates around printing location assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 06:06:44 +01:00
snipe
f29146b319 Fixed asset file listing display at API endpoint
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 05:53:24 +01:00
snipe
ce0bd68716 RMB for file routes
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 05:52:59 +01:00
snipe
f3f26b3824 Added UploadedFilesTransformer
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 05:52:48 +01:00
snipe
7be3d6072f Removed erroneous comment
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 05:52:35 +01:00
snipe
1d030b59df Merge pull request #16592 from fvollmer/anonymous-ldap
Improve Settings: Remove username and password requirement for ldap
2025-04-09 05:19:09 +01:00
snipe
07d2d8c549 Merge pull request #16669 from snipe/translations_for_skins
Fixed  #16130 - Added translations for skins, other settings
2025-04-09 02:37:17 +01:00
snipe
691ccbbebc Additional branding placeholders
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 02:32:22 +01:00
snipe
9daa09277d Added translations for skins
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 02:23:18 +01:00
snipe
79eaf62c9e Merge pull request #16668 from spencerrlongg/bug/sc-28768
Handle Potentially Unsafe File Output Better During Restore
2025-04-09 01:01:59 +01:00
spencerrlongg
dd078785ac always show unsafe files 2025-04-08 18:06:18 -05:00
spencerrlongg
12a8c54331 Handle and log skipped unsafe files during restore
Introduced a mechanism to track and log potentially unsafe files skipped during the restore process. These files are collected in an array and displayed as warnings before exiting, improving transparency and debugging capability.
2025-04-08 16:43:36 -05:00
Godfrey M
715fc2de59 more cleaning 2025-04-08 11:58:23 -07:00
Godfrey M
af53559ca3 cleaned up code 2025-04-08 11:54:50 -07:00
Godfrey M
17b8ea9c86 removed unnecessary code 2025-04-08 11:51:29 -07:00
Godfrey M
1ad96e891b added option logic to licenses 2025-04-08 11:50:45 -07:00
Jeremy Price
4e2b4195b4 Grokability-ize .well-known/security.txt 2025-04-08 11:50:03 -07:00
Godfrey M
b7492928ad reworked checkedInBy, added option logic to accessories 2025-04-08 11:43:02 -07:00
snipe
9f04254963 Merge pull request #16666 from snipe/print_view_tweaks
Added signature to licenses in print view, misc other fixes
2025-04-08 19:34:02 +01:00
akemidx
7701c6097f adding username split 2025-04-08 13:21:32 -04:00
akemidx
275e1beda2 updating for conflicts and adding test 2025-04-08 13:15:28 -04:00
Godfrey M
85b67dbb71 adds redirect to user option from checkin blades 2025-04-08 10:13:27 -07:00
akemidx
f659b7631d Merge branch 'develop' into lastnameemail 2025-04-08 13:10:26 -04:00
Godfrey M
a602b2fd47 adds option to redirect back to checkedInBy user for assets 2025-04-08 10:09:43 -07:00
fvollmer
a56f6148fc Improve Settings: Remove username and password requirement for ldap
Since 9d62793 anonymous LDAP login is available. Remove username and password requirement in settings dialog.
2025-04-08 11:04:37 +02:00
akemidx
f0bf77735d adding last name only as a email choice 2025-04-03 23:34:04 -04:00
Jeremy Price
afd9282785 Update remaining comments from snipe/snipe-it to grokability/snipe-it 2025-04-03 12:07:05 -07:00
Jeremy Price
1344ed1d16 Update app & composer files from snipe/snipe-it to grokability/snipe-it 2025-04-03 12:04:12 -07:00
Jeremy Price
cbea096403 Update Vagrantfile files from snipe/snipe-it to grokability/snipe-it 2025-04-03 12:02:42 -07:00
Jeremy Price
094edbd114 Update css and less files from snipe/snipe-it to grokability/snipe-it 2025-04-03 12:00:51 -07:00
Jeremy Price
f016b6b988 Update relevant README sections from snipe/snipe-it to grokability/snipe-it 2025-04-03 11:54:39 -07:00
Jeremy Price
782b35e0f1 Update scripts from snipe/snipe-it to grokability/snipe-it 2025-04-03 11:51:12 -07:00
Jeremy Price
70e9c6b947 Update relevant snipe/snipe-it references to grokability/snipe-it in Docker files 2025-04-03 11:44:27 -07:00
Jeremy Price
4a457c96e8 Update Contributors links from snipe/snipe-it to grokability/snipe-it 2025-04-03 11:39:21 -07:00
Jeremy Price
2e2516825e Update github workflows from snipe/snipe-it to grokability/snipe-it 2025-04-03 11:36:57 -07:00
akemidx
392db81499 requested changes 2025-04-02 21:28:34 -04:00
Godfrey M
4ef161214d notification icon only appears when there are notifications 2025-04-01 12:01:35 -07:00
Godfrey M
29d0380db3 reword warning messages, remove warning if webhook cleared and saved, deprecations only for superadmins 2025-04-01 11:53:32 -07:00
akemidx
d96498ab1f this is a redo after a borked rebase 2025-03-31 18:33:20 -04:00
Marcus Moore
e6ccff103f Add simple null check to avoid attempting to transform missing relationship 2025-03-19 12:52:01 -07:00
akemidx
e408b902f0 removing depreciation from purchase cost (unneeded, should go elsewhere if wanted) 2025-03-18 16:38:44 -04:00
Jeremy Price
3d2d7684aa Add console command to disable SAML logins
If a Snipe-IT sire has SAML enabled, and the SAML config is sufficiently
borked, the site will fail to even load the login page. That's probably
something that should be examined, but in the meantime, it'd be handy to
not have to manually edit the database to turn off SAML.

In this commit, I'm creating a saml-disable console command. And by
create, i mean i'm copypasta-ing the existing ldap-disable command.
2025-03-17 19:58:21 -07:00
Marcus Moore
d5bc5caacd Purge activity log of consumable bulk checkins 2025-03-12 11:57:18 -07:00
Marcus Moore
be6caf936e Avoid logging consumable checkins 2025-03-12 11:48:14 -07:00
snipe
0b48fd1465 Removed extra headers
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 13:05:31 +00:00
snipe
220537fbfb Updated presenter name
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 12:59:57 +00:00
snipe
df5437647b Add optional serial value in presenter
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 12:43:38 +00:00
snipe
92b2da9b1b Added history tab to components
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 11:48:38 +00:00
snipe
ef56177372 Use presenter
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 11:48:31 +00:00
snipe
cb7822576f Use new presenters
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 11:48:19 +00:00
snipe
7ba361b10d Use date formatter for filestable
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 10:57:54 +00:00
snipe
55694fa2fc Added strings
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 10:57:40 +00:00
snipe
c825878c46 Added history presenter
Signed-off-by: snipe <snipe@snipe.net>
2025-03-10 10:57:33 +00:00
snipe
80a69bfe90 Revert datetime to date
Signed-off-by: snipe <snipe@snipe.net>
2025-03-06 18:09:27 +00:00
snipe
d4dc8d2b79 Remove action_date from loggable as a changed field
Signed-off-by: snipe <snipe@snipe.net>
2025-03-06 17:43:07 +00:00
snipe
4e3df93349 Change action_date display to date from datetime
Signed-off-by: snipe <snipe@snipe.net>
2025-03-06 16:16:17 +00:00
snipe
38efc62900 Add index on action_date, copy from created_at
Signed-off-by: snipe <snipe@snipe.net>
2025-03-06 16:01:46 +00:00
Marcus Moore
d1683d1c65 Use existing translation string 2025-03-05 16:10:36 -08:00
Marcus Moore
f038254038 Have UI reflect not being able to delete accessory 2025-03-05 16:02:23 -08:00
Marcus Moore
a19582a5f3 Prevent deleting accessory that has checkouts via api 2025-03-05 15:58:34 -08:00
Marcus Moore
00cbebd1e3 Add failing test for api 2025-03-05 15:57:18 -08:00
Marcus Moore
8c21d625fc Prevent deleting accessory that has checkouts via UI 2025-03-05 15:56:01 -08:00
snipe
fecee69de6 Use table name to avoid ambiguous query
Signed-off-by: snipe <snipe@snipe.net>
2025-03-05 00:43:04 +00:00
snipe
75366927f0 Fixed table name
Signed-off-by: snipe <snipe@snipe.net>
2025-03-05 00:41:11 +00:00
snipe
5ece721b00 Check for FMCS
Signed-off-by: snipe <snipe@snipe.net>
2025-02-23 15:02:45 +00:00
snipe
0e2251c810 Scope selectlist by company
Signed-off-by: snipe <snipe@snipe.net>
2025-02-23 14:59:05 +00:00
Brady Wetherington
eccdcc373e parent 2220828b00
author Brady Wetherington <bwetherington@grokability.com> 1728320853 +0100
committer Brady Wetherington <bwetherington@grokability.com> 1733158021 +0000

Prevent setting assigned_to without setting assigned_type

Fixed tests to include assigned_type when setting assigned_to

Add new tests for assigned_to without assigned_type

Added tighter validation to assigned_to and assigned_type, new tests

Fixed wrong comment

Fixed tests to include assigned_type when setting assigned_to

Add new tests for assigned_to without assigned_type

Fixed wrong comment
2024-12-02 16:53:08 +00:00
snipe
5cdb52a249 Corrected flag
Signed-off-by: snipe <snipe@snipe.net>
2024-08-29 21:33:08 +01:00
snipe
ea38d6c2f3 Decode as integers
Signed-off-by: snipe <snipe@snipe.net>
2024-08-29 21:26:38 +01:00
Timo Schwarzer
a2ff8f9609 Add Department Manager to single and multiple user views 2024-07-15 12:27:29 +02:00
2589 changed files with 63421 additions and 22507 deletions

View File

@@ -2590,10 +2590,10 @@
]
},
{
"login": "QveenSi",
"login": "qveensi",
"name": "Yevhenii Huzii",
"avatar_url": "https://avatars.githubusercontent.com/u/19945501?v=4",
"profile": "https://github.com/QveenSi",
"profile": "https://github.com/qveensi",
"contributions": [
"code"
]
@@ -2607,15 +2607,6 @@
"code"
]
},
{
"login": "QveenSi",
"name": "Yevhenii Huzii",
"avatar_url": "https://avatars.githubusercontent.com/u/19945501?v=4",
"profile": "https://github.com/QveenSi",
"contributions": [
"code"
]
},
{
"login": "chrisweirich",
"name": "Christian Weirich",
@@ -3316,6 +3307,888 @@
"contributions": [
"code"
]
},
{
"login": "fvollmer",
"name": "fvollmer",
"avatar_url": "https://avatars.githubusercontent.com/u/16699443?v=4",
"profile": "https://github.com/fvollmer",
"contributions": [
"code"
]
},
{
"login": "36864",
"name": "36864",
"avatar_url": "https://avatars.githubusercontent.com/u/109086466?v=4",
"profile": "https://github.com/36864",
"contributions": [
"code"
]
},
{
"login": "CloCkWeRX",
"name": "Daniel O'Connor",
"avatar_url": "https://avatars.githubusercontent.com/u/365751?v=4",
"profile": "http://clockwerx.blogspot.com/",
"contributions": [
"code"
]
},
{
"login": "BeatSpark",
"name": "BeatSpark",
"avatar_url": "https://avatars.githubusercontent.com/u/102852568?v=4",
"profile": "https://github.com/BeatSpark",
"contributions": [
"code"
]
},
{
"login": "mrdahbi",
"name": "mrdahbi",
"avatar_url": "https://avatars.githubusercontent.com/u/59203607?v=4",
"profile": "https://github.com/mrdahbi",
"contributions": [
"code"
]
},
{
"login": "chfsx",
"name": "Fabian Schmid",
"avatar_url": "https://avatars.githubusercontent.com/u/6661332?v=4",
"profile": "http://sr.solutions",
"contributions": [
"code"
]
},
{
"login": "realchrisolin",
"name": "Chris Olin",
"avatar_url": "https://avatars.githubusercontent.com/u/1288116?v=4",
"profile": "https://www.chrisolin.com",
"contributions": [
"code"
]
},
{
"login": "mnemonicly",
"name": "Dan",
"avatar_url": "https://avatars.githubusercontent.com/u/3803132?v=4",
"profile": "https://github.com/mnemonicly",
"contributions": [
"code"
]
},
{
"login": "NebelKreis",
"name": "Nebel",
"avatar_url": "https://avatars.githubusercontent.com/u/43917728?v=4",
"profile": "https://github.com/NebelKreis",
"contributions": [
"code"
]
},
{
"login": "test1337ahp",
"name": "test1337ahp",
"avatar_url": "https://avatars.githubusercontent.com/u/132433803?v=4",
"profile": "https://github.com/test1337ahp",
"contributions": [
"code"
]
},
{
"login": "JonathonReinhart",
"name": "Jonathon Reinhart",
"avatar_url": "https://avatars.githubusercontent.com/u/1916566?v=4",
"profile": "https://github.com/JonathonReinhart",
"contributions": [
"code"
]
},
{
"login": "aranar-pro",
"name": "aranar-pro",
"avatar_url": "https://avatars.githubusercontent.com/u/484742?v=4",
"profile": "https://github.com/aranar-pro",
"contributions": [
"code"
]
},
{
"login": "phil-flip",
"name": "Phil",
"avatar_url": "https://avatars.githubusercontent.com/u/27019397?v=4",
"profile": "https://github.com/phil-flip",
"contributions": [
"code"
]
},
{
"login": "fe80",
"name": "Steffy Fort",
"avatar_url": "https://avatars.githubusercontent.com/u/6473460?v=4",
"profile": "https://fe80.fr/",
"contributions": [
"code"
]
},
{
"login": "sorvani",
"name": "Jared Busch",
"avatar_url": "https://avatars.githubusercontent.com/u/3302372?v=4",
"profile": "https://github.com/sorvani",
"contributions": [
"code"
]
},
{
"login": "seanborg-codethink",
"name": "seanborg-codethink",
"avatar_url": "https://avatars.githubusercontent.com/u/111956991?v=4",
"profile": "https://github.com/seanborg-codethink",
"contributions": [
"code"
]
},
{
"login": "dkaatz",
"name": "dkaatz",
"avatar_url": "https://avatars.githubusercontent.com/u/160669961?v=4",
"profile": "https://github.com/dkaatz",
"contributions": [
"code"
]
},
{
"login": "DanielRuf",
"name": "Daniel Ruf",
"avatar_url": "https://avatars.githubusercontent.com/u/827205?v=4",
"profile": "https://threema.id/74SF7MW6?text=",
"contributions": [
"code"
]
},
{
"login": "ahpaleus",
"name": "ahpaleus",
"avatar_url": "https://avatars.githubusercontent.com/u/38883201?v=4",
"profile": "https://github.com/ahpaleus",
"contributions": [
"code"
]
},
{
"login": "mink-adao-duy",
"name": "Anh DAO-DUY",
"avatar_url": "https://avatars.githubusercontent.com/u/22906055?v=4",
"profile": "https://github.com/mink-adao-duy",
"contributions": [
"code"
]
},
{
"login": "Serdnad",
"name": "Andres Gutierrez",
"avatar_url": "https://avatars.githubusercontent.com/u/4723453?v=4",
"profile": "https://github.com/Serdnad",
"contributions": [
"code"
]
},
{
"login": "wewhite",
"name": "Warren White",
"avatar_url": "https://avatars.githubusercontent.com/u/111083379?v=4",
"profile": "https://github.com/wewhite",
"contributions": [
"code"
]
},
{
"login": "robintemme",
"name": "Robin Temme",
"avatar_url": "https://avatars.githubusercontent.com/u/2809241?v=4",
"profile": "https://robintemme.de/",
"contributions": [
"code"
]
},
{
"login": "herroworrd",
"name": "herroworrd",
"avatar_url": "https://avatars.githubusercontent.com/u/47008367?v=4",
"profile": "https://github.com/herroworrd",
"contributions": [
"code"
]
},
{
"login": "vicleos",
"name": "vicleos",
"avatar_url": "https://avatars.githubusercontent.com/u/28558609?v=4",
"profile": "https://mubiu.com/",
"contributions": [
"code"
]
},
{
"login": "thinkl33t",
"name": "Bob Clough",
"avatar_url": "https://avatars.githubusercontent.com/u/1016780?v=4",
"profile": "http://thinkl33t.co.uk/",
"contributions": [
"code"
]
},
{
"login": "brandon-bailey",
"name": "Brandon Daniel Bailey",
"avatar_url": "https://avatars.githubusercontent.com/u/10648463?v=4",
"profile": "https://github.com/brandon-bailey",
"contributions": [
"code"
]
},
{
"login": "marcquark",
"name": "Marc Bartelt",
"avatar_url": "https://avatars.githubusercontent.com/u/23556080?v=4",
"profile": "https://github.com/marcquark",
"contributions": [
"code"
]
},
{
"login": "manu-crealytics",
"name": "manu-crealytics",
"avatar_url": "https://avatars.githubusercontent.com/u/18286893?v=4",
"profile": "https://github.com/manu-crealytics",
"contributions": [
"code"
]
},
{
"login": "Galaxy102",
"name": "Konstantin Köhring",
"avatar_url": "https://avatars.githubusercontent.com/u/18245993?v=4",
"profile": "https://www.galaxy102.de/",
"contributions": [
"code"
]
},
{
"login": "deloz",
"name": "Deloz",
"avatar_url": "https://avatars.githubusercontent.com/u/685167?v=4",
"profile": "https://deloz.net/",
"contributions": [
"code"
]
},
{
"login": "mbrrg",
"name": "Martin Berg",
"avatar_url": "https://avatars.githubusercontent.com/u/2682426?v=4",
"profile": "https://github.com/mbrrg",
"contributions": [
"code"
]
},
{
"login": "Nothing4You",
"name": "Richard Schwab",
"avatar_url": "https://avatars.githubusercontent.com/u/3694534?v=4",
"profile": "https://github.com/Nothing4You",
"contributions": [
"code"
]
},
{
"login": "rickheil",
"name": "Rick Heil",
"avatar_url": "https://avatars.githubusercontent.com/u/8959676?v=4",
"profile": "https://rickheil.com/",
"contributions": [
"code"
]
},
{
"login": "rosscdh",
"name": "Ross Crawford-d'Heureuse",
"avatar_url": "https://avatars.githubusercontent.com/u/397106?v=4",
"profile": "https://github.com/rosscdh",
"contributions": [
"code"
]
},
{
"login": "McG800",
"name": "Ryan McGuire",
"avatar_url": "https://avatars.githubusercontent.com/u/1621107?v=4",
"profile": "https://github.com/McG800",
"contributions": [
"code"
]
},
{
"login": "SBrown2021",
"name": "SBrown2021",
"avatar_url": "https://avatars.githubusercontent.com/u/77835667?v=4",
"profile": "https://github.com/SBrown2021",
"contributions": [
"code"
]
},
{
"login": "serkanerip",
"name": "Serkan",
"avatar_url": "https://avatars.githubusercontent.com/u/8780913?v=4",
"profile": "https://github.com/serkanerip",
"contributions": [
"code"
]
},
{
"login": "Shankschn",
"name": "Shanks",
"avatar_url": "https://avatars.githubusercontent.com/u/63188620?v=4",
"profile": "https://www.yudelei.com/",
"contributions": [
"code"
]
},
{
"login": "cendai-mis",
"name": "cendai-mis",
"avatar_url": "https://avatars.githubusercontent.com/u/198525698?v=4",
"profile": "https://github.com/cendai-mis",
"contributions": [
"code"
]
},
{
"login": "smcpeck",
"name": "Shaun McPeck",
"avatar_url": "https://avatars.githubusercontent.com/u/8724583?v=4",
"profile": "https://smcpeck.github.io/",
"contributions": [
"code"
]
},
{
"login": "snazy2000",
"name": "Stephen",
"avatar_url": "https://avatars.githubusercontent.com/u/1378836?v=4",
"profile": "https://github.com/snazy2000",
"contributions": [
"code"
]
},
{
"login": "Nevets82",
"name": "Steven",
"avatar_url": "https://avatars.githubusercontent.com/u/4462739?v=4",
"profile": "http://nevets82.github.io/",
"contributions": [
"code"
]
},
{
"login": "Mateus-Romera",
"name": "Mateus Villar",
"avatar_url": "https://avatars.githubusercontent.com/u/29017267?v=4",
"profile": "https://mateusvillar.com/",
"contributions": [
"code"
]
},
{
"login": "mzack5020",
"name": "Matthew Zackschewski",
"avatar_url": "https://avatars.githubusercontent.com/u/12749393?v=4",
"profile": "https://github.com/mzack5020",
"contributions": [
"code"
]
},
{
"login": "firefrei",
"name": "Matthias Frei",
"avatar_url": "https://avatars.githubusercontent.com/u/12660103?v=4",
"profile": "https://www.frei.media/",
"contributions": [
"code"
]
},
{
"login": "nticaric",
"name": "Nenad Ticaric",
"avatar_url": "https://avatars.githubusercontent.com/u/824840?v=4",
"profile": "https://github.com/nticaric",
"contributions": [
"code"
]
},
{
"login": "Scorcher",
"name": "Nikolay Didenko",
"avatar_url": "https://avatars.githubusercontent.com/u/706439?v=4",
"profile": "https://github.com/Scorcher",
"contributions": [
"code"
]
},
{
"login": "nunomaduro",
"name": "Nuno Maduro",
"avatar_url": "https://avatars.githubusercontent.com/u/5457236?v=4",
"profile": "https://nunomaduro.com/sponsorships",
"contributions": [
"code"
]
},
{
"login": "owalerys",
"name": "Oliver Walerys",
"avatar_url": "https://avatars.githubusercontent.com/u/8883074?v=4",
"profile": "https://tektikhq.com/",
"contributions": [
"code"
]
},
{
"login": "rcmcdonald91",
"name": "R. Christian McDonald",
"avatar_url": "https://avatars.githubusercontent.com/u/3102039?v=4",
"profile": "https://keybase.io/rcmcdonald91",
"contributions": [
"code"
]
},
{
"login": "nixn",
"name": "nix",
"avatar_url": "https://avatars.githubusercontent.com/u/1525581?v=4",
"profile": "https://nnix.net/",
"contributions": [
"code"
]
},
{
"login": "octobunny",
"name": "octobunny",
"avatar_url": "https://avatars.githubusercontent.com/u/55462380?v=4",
"profile": "https://github.com/octobunny",
"contributions": [
"code"
]
},
{
"login": "sreyemnayr",
"name": "Ryan",
"avatar_url": "https://avatars.githubusercontent.com/u/8558670?v=4",
"profile": "https://github.com/sreyemnayr",
"contributions": [
"code"
]
},
{
"login": "p3nj",
"name": "p3nj",
"avatar_url": "https://avatars.githubusercontent.com/u/1501022?v=4",
"profile": "https://benji.ltd/",
"contributions": [
"code"
]
},
{
"login": "timwsuqld",
"name": "Tim White",
"avatar_url": "https://avatars.githubusercontent.com/u/6201617?v=4",
"profile": "https://github.com/timwsuqld",
"contributions": [
"code"
]
},
{
"login": "yannikp",
"name": "yannikp",
"avatar_url": "https://avatars.githubusercontent.com/u/22473767?v=4",
"profile": "https://github.com/yannikp",
"contributions": [
"code"
]
},
{
"login": "viclou",
"name": "victoria",
"avatar_url": "https://avatars.githubusercontent.com/u/20525448?v=4",
"profile": "https://github.com/viclou",
"contributions": [
"code"
]
},
{
"login": "valentyntu",
"name": "Valentyn Tulub",
"avatar_url": "https://avatars.githubusercontent.com/u/40685314?v=4",
"profile": "https://github.com/valentyntu",
"contributions": [
"code"
]
},
{
"login": "Wouter0100",
"name": "Wouter van Os",
"avatar_url": "https://avatars.githubusercontent.com/u/864520?v=4",
"profile": "http://wouter0100.nl/",
"contributions": [
"code"
]
},
{
"login": "xWyatt",
"name": "Wyatt Teeter",
"avatar_url": "https://avatars.githubusercontent.com/u/3946540?v=4",
"profile": "https://www.linkedin.com/in/wyatt-teeter",
"contributions": [
"code"
]
},
{
"login": "terwey",
"name": "Yorick Terweijden",
"avatar_url": "https://avatars.githubusercontent.com/u/1596124?v=4",
"profile": "https://github.com/terwey",
"contributions": [
"code"
]
},
{
"login": "bmkalle",
"name": "bmkalle",
"avatar_url": "https://avatars.githubusercontent.com/u/69298836?v=4",
"profile": "https://github.com/bmkalle",
"contributions": [
"code"
]
},
{
"login": "bricelabelle",
"name": "bricelabelle",
"avatar_url": "https://avatars.githubusercontent.com/u/28403467?v=4",
"profile": "https://github.com/bricelabelle",
"contributions": [
"code"
]
},
{
"login": "corydlamb",
"name": "corydlamb",
"avatar_url": "https://avatars.githubusercontent.com/u/97770090?v=4",
"profile": "https://github.com/corydlamb",
"contributions": [
"code"
]
},
{
"login": "splashx",
"name": "Diogenes S. Jesus",
"avatar_url": "https://avatars.githubusercontent.com/u/1154133?v=4",
"profile": "http://twitter.com/splash",
"contributions": [
"code"
]
},
{
"login": "dkmansion",
"name": "D M",
"avatar_url": "https://avatars.githubusercontent.com/u/5826629?v=4",
"profile": "https://github.com/dkmansion",
"contributions": [
"code"
]
},
{
"login": "Jarli01",
"name": "Dustin B",
"avatar_url": "https://avatars.githubusercontent.com/u/14837699?v=4",
"profile": "https://github.com/Jarli01",
"contributions": [
"code"
]
},
{
"login": "fabiang",
"name": "Fabian Grutschus",
"avatar_url": "https://avatars.githubusercontent.com/u/348344?v=4",
"profile": "https://github.com/fabiang",
"contributions": [
"code"
]
},
{
"login": "MelonSmasher",
"name": "MelonSmasher",
"avatar_url": "https://avatars.githubusercontent.com/u/1491053?v=4",
"profile": "https://github.com/MelonSmasher",
"contributions": [
"code"
]
},
{
"login": "AlexanderWPapyrus",
"name": "AlexanderWPapyrus",
"avatar_url": "https://avatars.githubusercontent.com/u/80526133?v=4",
"profile": "https://github.com/AlexanderWPapyrus",
"contributions": [
"code"
]
},
{
"login": "disc",
"name": "Alexandr Hacicheant",
"avatar_url": "https://avatars.githubusercontent.com/u/306231?v=4",
"profile": "https://github.com/disc",
"contributions": [
"code"
]
},
{
"login": "hex128",
"name": "Hex",
"avatar_url": "https://avatars.githubusercontent.com/u/3032891?v=4",
"profile": "https://hex128.io/",
"contributions": [
"code"
]
},
{
"login": "arukompas",
"name": "Arunas Skirius",
"avatar_url": "https://avatars.githubusercontent.com/u/8697942?v=4",
"profile": "https://github.com/arukompas",
"contributions": [
"code"
]
},
{
"login": "benperiton",
"name": "Ben Periton",
"avatar_url": "https://avatars.githubusercontent.com/u/104396?v=4",
"profile": "https://github.com/benperiton",
"contributions": [
"code"
]
},
{
"login": "byronwolfman",
"name": "Byron Wolfman",
"avatar_url": "https://avatars.githubusercontent.com/u/11906832?v=4",
"profile": "https://wolfman.dev/",
"contributions": [
"code"
]
},
{
"login": "CalvinSchwartz",
"name": "Calvin",
"avatar_url": "https://avatars.githubusercontent.com/u/56485508?v=4",
"profile": "https://github.com/CalvinSchwartz",
"contributions": [
"code"
]
},
{
"login": "juanfont",
"name": "Juan Font",
"avatar_url": "https://avatars.githubusercontent.com/u/181059?v=4",
"profile": "https://github.com/juanfont",
"contributions": [
"code"
]
},
{
"login": "juhotaipale",
"name": "Juho Taipale",
"avatar_url": "https://avatars.githubusercontent.com/u/13137708?v=4",
"profile": "https://github.com/juhotaipale",
"contributions": [
"code"
]
},
{
"login": "KorvinSzanto",
"name": "Korvin Szanto",
"avatar_url": "https://avatars.githubusercontent.com/u/1007419?v=4",
"profile": "https://github.com/KorvinSzanto",
"contributions": [
"code"
]
},
{
"login": "sniff122",
"name": "Lewis Foster",
"avatar_url": "https://avatars.githubusercontent.com/u/8513053?v=4",
"profile": "https://lewisfoster.foo/",
"contributions": [
"code"
]
},
{
"login": "loganswartz",
"name": "Logan Swartzendruber",
"avatar_url": "https://avatars.githubusercontent.com/u/33877541?v=4",
"profile": "https://github.com/loganswartz",
"contributions": [
"code"
]
},
{
"login": "lopezio",
"name": "Lorenzo P.",
"avatar_url": "https://avatars.githubusercontent.com/u/1156208?v=4",
"profile": "https://github.com/lopezio",
"contributions": [
"code"
]
},
{
"login": "m4us1ne",
"name": "Lukas Jung",
"avatar_url": "https://avatars.githubusercontent.com/u/33946590?v=4",
"profile": "https://github.com/m4us1ne",
"contributions": [
"code"
]
},
{
"login": "LeafedFox",
"name": "Ellie",
"avatar_url": "https://avatars.githubusercontent.com/u/10965027?v=4",
"profile": "https://leafedfox.xyz/",
"contributions": [
"code"
]
},
{
"login": "gastamper",
"name": "GA Stamper",
"avatar_url": "https://avatars.githubusercontent.com/u/20960555?v=4",
"profile": "https://github.com/gastamper",
"contributions": [
"code"
]
},
{
"login": "gl-pup",
"name": "Guillaume Lefranc",
"avatar_url": "https://avatars.githubusercontent.com/u/206553556?v=4",
"profile": "https://github.com/gl-pup",
"contributions": [
"code"
]
},
{
"login": "dasjoe",
"name": "Hajo Möller",
"avatar_url": "https://avatars.githubusercontent.com/u/733892?v=4",
"profile": "https://github.com/dasjoe",
"contributions": [
"code"
]
},
{
"login": "pottom",
"name": "Istvan Basa",
"avatar_url": "https://avatars.githubusercontent.com/u/3420063?v=4",
"profile": "https://github.com/pottom",
"contributions": [
"code"
]
},
{
"login": "jjasghar",
"name": "JJ Asghar",
"avatar_url": "https://avatars.githubusercontent.com/u/810824?v=4",
"profile": "https://jjasghar.github.io/",
"contributions": [
"code"
]
},
{
"login": "JemCdo",
"name": "James E. Msenga",
"avatar_url": "https://avatars.githubusercontent.com/u/40404495?v=4",
"profile": "https://github.com/JemCdo",
"contributions": [
"code"
]
},
{
"login": "jfwiebe",
"name": "Jan Felix Wiebe",
"avatar_url": "https://avatars.githubusercontent.com/u/6865786?v=4",
"profile": "https://github.com/jfwiebe",
"contributions": [
"code"
]
},
{
"login": "drexljo",
"name": "Jo Drexl",
"avatar_url": "https://avatars.githubusercontent.com/u/43412008?v=4",
"profile": "https://www.nfon.com/",
"contributions": [
"code"
]
},
{
"login": "austinsasko",
"name": "Austin Sasko",
"avatar_url": "https://avatars.githubusercontent.com/u/4807843?v=4",
"profile": "https://github.com/austinsasko",
"contributions": [
"code"
]
},
{
"login": "JassonCordones",
"name": "Jasson",
"avatar_url": "https://avatars.githubusercontent.com/u/4875039?v=4",
"profile": "http://jassoncordones.github.io",
"contributions": [
"code"
]
},
{
"login": "Tinyblargon",
"name": "Okean",
"avatar_url": "https://avatars.githubusercontent.com/u/76069640?v=4",
"profile": "https://github.com/Tinyblargon",
"contributions": [
"code"
]
},
{
"login": "amedranogil",
"name": "Alejandro Medrano",
"avatar_url": "https://avatars.githubusercontent.com/u/6515064?v=4",
"profile": "https://www.lst.tfo.upm.es/alejandro-medrano/",
"contributions": [
"code"
]
},
{
"login": "lukaskraic",
"name": "Lukas Kraic",
"avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
"profile": "https://github.com/lukaskraic",
"contributions": [
"code"
]
}
]
}

View File

@@ -79,6 +79,13 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS

View File

@@ -28,6 +28,7 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=db
DB_SOCKET=null
DB_PORT='3306'
DB_DATABASE=snipeit
DB_USERNAME=snipeit
@@ -84,6 +85,15 @@ MAIL_BACKUP_NOTIFICATION_DRIVER=null
MAIL_BACKUP_NOTIFICATION_ADDRESS=null
BACKUP_ENV=true
# --------------------------------------------
# OPTIONAL: CHANGE PHP UPLOAD LIMITS (UNCOMMENT WHEN NEEDING TO BE CHANGED)
# --------------------------------------------
#PHP_UPLOAD_LIMIT=10
#PHP_POST_MAX_SIZE=10
#PHP_UPLOAD_MAX_FILESIZE=10
#PHP_MEMORY_LIMIT=10
# --------------------------------------------
# OPTIONAL: SESSION SETTINGS
# --------------------------------------------
@@ -159,6 +169,7 @@ AWS_DEFAULT_REGION=null
LOGIN_MAX_ATTEMPTS=5
LOGIN_LOCKOUT_DURATION=60
RESET_PASSWORD_LINK_EXPIRES=900
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC

View File

@@ -24,6 +24,7 @@ PUBLIC_FILESYSTEM_DISK=local_public
# --------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_SOCKET=null
DB_PORT=3306
DB_DATABASE=null
DB_USERNAME=null
@@ -174,11 +175,13 @@ LOGIN_AUTOCOMPLETE=false
RESET_PASSWORD_LINK_EXPIRES=15
PASSWORD_CONFIRM_TIMEOUT=10800
PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50
INVITE_PASSWORD_LINK_EXPIRES=1500
# --------------------------------------------
# OPTIONAL: MISC
# --------------------------------------------
LOG_CHANNEL=single
LOG_DEPRECATIONS=false
LOG_MAX_DAYS=10
APP_LOCKED=false
APP_CIPHER=AES-256-CBC

View File

@@ -10,10 +10,10 @@ name: Codacy Security Scan
on:
push:
branches: [ master ]
branches: [ develop ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
branches: [ develop ]
schedule:
- cron: '36 23 * * 3'
@@ -22,11 +22,11 @@ permissions:
jobs:
codacy-security-scan:
# Ensure schedule job never runs on forked repos. It's only executed for 'snipe/snipe-it'
# Ensure schedule job never runs on forked repos. It's only executed for 'grokability/snipe-it'
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
if: (github.repository == 'snipe/snipe-it') || ((github.repository != 'snipe/snipe-it') && (github.event_name != 'schedule'))
if: (github.repository == 'grokability/snipe-it') || ((github.repository != 'grokability/snipe-it') && (github.event_name != 'schedule'))
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
@@ -36,7 +36,7 @@ jobs:
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@v4.4.5
uses: codacy/codacy-analysis-cli-action@v4.4.7
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations

View File

@@ -20,8 +20,8 @@ permissions:
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
if: github.repository == 'snipe/snipe-it'
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
@@ -32,7 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=tag,suffix=-alpine
type=semver,pattern=v{{major}}-latest-alpine
type=semver,pattern=v{{major}}-latest-alpine
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.

View File

@@ -1,5 +1,5 @@
# Snipe-IT Docker image build for hub.docker.com
name: Docker images
name: Docker images (Ubuntu)
# Run this Build for all pushes to 'master' or develop branch, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process
@@ -20,8 +20,8 @@ permissions:
jobs:
docker:
# Ensure this job never runs on forked repos. It's only executed for 'snipe/snipe-it'
if: github.repository == 'snipe/snipe-it'
# Ensure this job never runs on forked repos. It's only executed for 'grokability/snipe-it'
if: github.repository == 'grokability/snipe-it'
runs-on: ubuntu-latest
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
@@ -32,7 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
type=semver,pattern=v{{major}}-latest
type=semver,pattern=v{{major}}-latest
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.

240
.pa11yci.json Normal file
View File

@@ -0,0 +1,240 @@
{
"standard": "WCAG2AA",
"level": "error",
"defaults": {
"useIncognitoBrowserContext": false,
"timeout": 500000,
"wait": 5000,
"ignore" : [
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G145.Fail",
"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
],
"viewport": {
"width": 1280,
"height": 1024
}
},
"urls": [
{
"__NOTE" : "this should always be FIRST (if browser context is preserved)",
"url": "https://snipe-it.test/login",
"actions": [
"navigate to https://snipe-it.test/login",
"screen capture tests/pa11y/login.png",
"set field input[name='username'] to admin",
"set field input[name='password'] to password",
"click element button[type=submit]",
"wait for url to be https://snipe-it.test/",
"screen capture tests/pa11y/dashboard.png"
]
},
{
"url" : "https://snipe-it.test/admin",
"actions" : [
"navigate to https://snipe-it.test/admin",
"screen capture tests/pa11y/admin-settings.png"
]
},
{
"url" : "https://snipe-it.test/admin/branding",
"actions" : [
"navigate to https://snipe-it.test/admin/branding",
"screen capture tests/pa11y/admin-branding.png"
]
},
{
"url" : "https://snipe-it.test/admin/general",
"actions" : [
"navigate to https://snipe-it.test/admin/general",
"screen capture tests/pa11y/admin-general.png"
]
},
{
"url" : "https://snipe-it.test/hardware/create",
"actions" : [
"navigate to https://snipe-it.test/hardware/create",
"screen capture tests/pa11y/asset-create.png"
]
},
{
"url" : "https://snipe-it.test/hardware",
"actions" : [
"navigate to https://snipe-it.test/hardware",
"screen capture tests/pa11y/asset-list.png"
]
},
{
"url" : "https://snipe-it.test/hardware/1",
"actions" : [
"navigate to https://snipe-it.test/hardware/1",
"screen capture tests/pa11y/asset-detail.png"
]
},
{
"url" : "https://snipe-it.test/account/view-assets",
"actions" : [
"navigate to https://snipe-it.test/account/view-assets",
"screen capture tests/pa11y/profile.png"
]
},
{
"url" : "https://snipe-it.test/licences",
"actions" : [
"navigate to https://snipe-it.test/licenses",
"screen capture tests/pa11y/license-list.png"
]
},
{
"url" : "https://snipe-it.test/licences/create",
"actions" : [
"navigate to https://snipe-it.test/licenses/create",
"screen capture tests/pa11y/license-create.png"
]
},
{
"url" : "https://snipe-it.test/licences/1",
"actions" : [
"navigate to https://snipe-it.test/licenses/1",
"screen capture tests/pa11y/license-view.png"
]
},
{
"url" : "https://snipe-it.test/consumables",
"actions" : [
"navigate to https://snipe-it.test/consumables",
"screen capture tests/pa11y/consumable-list.png"
]
},
{
"url" : "https://snipe-it.test/consumables/create",
"actions" : [
"navigate to https://snipe-it.test/consumables/create",
"screen capture tests/pa11y/consumable-create.png"
]
},
{
"url" : "https://snipe-it.test/consumables/1",
"actions" : [
"navigate to https://snipe-it.test/consumables/1",
"screen capture tests/pa11y/consumable-view.png"
]
},
{
"url" : "https://snipe-it.test/accessories",
"actions" : [
"navigate to https://snipe-it.test/accessories",
"screen capture tests/pa11y/accessory-list.png"
]
},
{
"url" : "https://snipe-it.test/accessories/create",
"actions" : [
"navigate to https://snipe-it.test/accessories/create",
"screen capture tests/pa11y/accessory-create.png"
]
},
{
"url" : "https://snipe-it.test/accessories/1",
"actions" : [
"navigate to https://snipe-it.test/accessories/1",
"screen capture tests/pa11y/accessory-view.png"
]
},
{
"url" : "https://snipe-it.test/locations",
"actions" : [
"navigate to https://snipe-it.test/locations",
"screen capture tests/pa11y/location-list.png"
]
},
{
"url" : "https://snipe-it.test/locations/create",
"actions" : [
"navigate to https://snipe-it.test/locations/create",
"screen capture tests/pa11y/location-create.png"
]
},
{
"url" : "https://snipe-it.test/locations/1",
"actions" : [
"navigate to https://snipe-it.test/locations/1",
"screen capture tests/pa11y/location-view.png"
]
},
{
"url" : "https://snipe-it.test/models",
"actions" : [
"navigate to https://snipe-it.test/models",
"screen capture tests/pa11y/model-list.png"
]
},
{
"url" : "https://snipe-it.test/models/create",
"actions" : [
"navigate to https://snipe-it.test/models/create",
"screen capture tests/pa11y/model-create.png"
]
},
{
"url" : "https://snipe-it.test/models/1",
"actions" : [
"navigate to https://snipe-it.test/models/1",
"screen capture tests/pa11y/model-view.png"
]
},
{
"url" : "https://snipe-it.test/companies",
"actions" : [
"navigate to https://snipe-it.test/companies",
"screen capture tests/pa11y/company-list.png"
]
},
{
"url" : "https://snipe-it.test/companies/create",
"actions" : [
"navigate to https://snipe-it.test/companies/create",
"screen capture tests/pa11y/company-create.png"
]
},
{
"url" : "https://snipe-it.test/companies/1",
"actions" : [
"navigate to https://snipe-it.test/companies/1",
"screen capture tests/pa11y/company-view.png"
]
},
{
"url" : "https://snipe-it.test/departments",
"actions" : [
"navigate to https://snipe-it.test/departments",
"screen capture tests/pa11y/department-list.png"
]
},
{
"url" : "https://snipe-it.test/departments/create",
"actions" : [
"navigate to https://snipe-it.test/departments/create",
"screen capture tests/pa11y/department-create.png"
]
},
{
"url" : "https://snipe-it.test/departments/1",
"actions" : [
"navigate to https://snipe-it.test/departments/1",
"screen capture tests/pa11y/department-view.png"
]
},
{
"url" : "https://snipe-it.test/invalid-url",
"actions" : [
"navigate to https://snipe-it.test/invalid-url",
"screen capture tests/pa11y/404.png"
]
}
]
}

View File

@@ -42,19 +42,32 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/1975640?v=4" width="110px;"/><br /><sub>Evan Taylor</sub>](https://github.com/Delta5)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Delta5 "Code") | [<img src="https://avatars.githubusercontent.com/u/8735148?v=4" width="110px;"/><br /><sub>Petri Asikainen</sub>](https://github.com/PetriAsi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PetriAsi "Code") | [<img src="https://avatars.githubusercontent.com/u/11424540?v=4" width="110px;"/><br /><sub>derdeagle</sub>](https://github.com/derdeagle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=derdeagle "Code") | [<img src="https://avatars.githubusercontent.com/u/176950?v=4" width="110px;"/><br /><sub>Mike Frysinger</sub>](https://wh0rd.org/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vapier "Code") | [<img src="https://avatars.githubusercontent.com/u/22044358?v=4" width="110px;"/><br /><sub>ALPHA</sub>](https://github.com/AL4AL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AL4AL "Code") | [<img src="https://avatars.githubusercontent.com/u/1042587?v=4" width="110px;"/><br /><sub>FliegenKLATSCH</sub>](https://www.ifern.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FliegenKLATSCH "Code") | [<img src="https://avatars.githubusercontent.com/u/442138?v=4" width="110px;"/><br /><sub>Jeremy Price</sub>](https://github.com/jerm)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jerm "Code") |
| [<img src="https://avatars.githubusercontent.com/u/84392209?v=4" width="110px;"/><br /><sub>Toreg87</sub>](https://github.com/Toreg87)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Toreg87 "Code") | [<img src="https://avatars.githubusercontent.com/u/67638596?v=4" width="110px;"/><br /><sub>Matthew Nickson</sub>](https://github.com/Computroniks)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Computroniks "Code") | [<img src="https://avatars.githubusercontent.com/u/1646397?v=4" width="110px;"/><br /><sub>Jethro Nederhof</sub>](https://jethron.id.au)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jethron "Code") | [<img src="https://avatars.githubusercontent.com/u/23289826?v=4" width="110px;"/><br /><sub>Oskar Stenberg</sub>](https://github.com/01ste02)<br />[💻](https://github.com/snipe/snipe-it/commits?author=01ste02 "Code") | [<img src="https://avatars.githubusercontent.com/u/82208283?v=4" width="110px;"/><br /><sub>Robert-Azelis</sub>](https://github.com/Robert-Azelis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Robert-Azelis "Code") | [<img src="https://avatars.githubusercontent.com/u/60648387?v=4" width="110px;"/><br /><sub>Alexander William Smith</sub>](https://github.com/alwism)<br />[💻](https://github.com/snipe/snipe-it/commits?author=alwism "Code") | [<img src="https://avatars.githubusercontent.com/u/24418301?v=4" width="110px;"/><br /><sub>LEITWERK AG</sub>](https://www.leitwerk.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=leitwerk-ag "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1911435?v=4" width="110px;"/><br /><sub>Adam</sub>](http://www.aboutcher.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adamboutcher "Code") | [<img src="https://avatars.githubusercontent.com/u/16104273?v=4" width="110px;"/><br /><sub>Ian</sub>](https://snksrv.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sneak-it "Code") | [<img src="https://avatars.githubusercontent.com/u/4023909?v=4" width="110px;"/><br /><sub>Shao Yu-Lung (Allen)</sub>](http://blog.bestlong.idv.tw/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bestlong "Code") | [<img src="https://avatars.githubusercontent.com/u/76475453?v=4" width="110px;"/><br /><sub>Haxatron</sub>](https://github.com/Haxatron)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Haxatron "Code") | [<img src="https://avatars.githubusercontent.com/u/88776392?v=4" width="110px;"/><br /><sub>PlaneNuts</sub>](https://github.com/PlaneNuts)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PlaneNuts "Code") | [<img src="https://avatars.githubusercontent.com/u/3842948?v=4" width="110px;"/><br /><sub>Bradley Coudriet</sub>](http://bjcpgd.cias.rit.edu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=exula "Code") | [<img src="https://avatars.githubusercontent.com/u/21966173?v=4" width="110px;"/><br /><sub>Dalton Durst</sub>](https://daltondur.st)<br />[💻](https://github.com/snipe/snipe-it/commits?author=UniversalSuperBox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/QveenSi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=QveenSi "Code") |
| [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") | [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") | [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") | [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") |
| [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") | [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") |
| [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") | [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/38761237?v=4" width="110px;"/><br /><sub>Alex Janes</sub>](https://adagiohealth.org)<br />[💻](https://github.com/snipe/snipe-it/commits?author=adagioajanes "Code") | [<img src="https://avatars.githubusercontent.com/u/32387849?v=4" width="110px;"/><br /><sub>Nuraeil</sub>](https://github.com/nuraeil)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nuraeil "Code") | [<img src="https://avatars.githubusercontent.com/u/48162670?v=4" width="110px;"/><br /><sub>TenOfTens</sub>](https://github.com/TenOfTens)<br />[💻](https://github.com/snipe/snipe-it/commits?author=TenOfTens "Code") | [<img src="https://avatars.githubusercontent.com/u/9415391?v=4" width="110px;"/><br /><sub>waffle</sub>](https://ditisjens.be/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=insert-waffle "Code") | [<img src="https://avatars.githubusercontent.com/u/19945501?v=4" width="110px;"/><br /><sub>Yevhenii Huzii</sub>](https://github.com/qveensi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qveensi "Code") | [<img src="https://avatars.githubusercontent.com/u/3839381?v=4" width="110px;"/><br /><sub>Achmad Fienan Rahardianto</sub>](https://github.com/veenone)<br />[💻](https://github.com/snipe/snipe-it/commits?author=veenone "Code") | [<img src="https://avatars.githubusercontent.com/u/97299851?v=4" width="110px;"/><br /><sub>Christian Weirich</sub>](https://github.com/chrisweirich)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chrisweirich "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1294403?v=4" width="110px;"/><br /><sub>denzfarid</sub>](https://github.com/denzfarid)<br /> | [<img src="https://avatars.githubusercontent.com/u/94018771?v=4" width="110px;"/><br /><sub>ntbutler-nbcs</sub>](https://github.com/ntbutler-nbcs)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntbutler-nbcs "Code") | [<img src="https://avatars.githubusercontent.com/u/172697?v=4" width="110px;"/><br /><sub>Naveen</sub>](https://naveensrinivasan.dev)<br />[💻](https://github.com/snipe/snipe-it/commits?author=naveensrinivasan "Code") | [<img src="https://avatars.githubusercontent.com/u/55674383?v=4" width="110px;"/><br /><sub>Mike Roquemore</sub>](https://github.com/mikeroq)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mikeroq "Code") | [<img src="https://avatars.githubusercontent.com/u/7991086?v=4" width="110px;"/><br /><sub>Daniel Reeder</sub>](https://github.com/reederda)<br />[🌍](#translation-reederda "Translation") [🌍](#translation-reederda "Translation") [💻](https://github.com/snipe/snipe-it/commits?author=reederda "Code") | [<img src="https://avatars.githubusercontent.com/u/109422491?v=4" width="110px;"/><br /><sub>vickyjaura183</sub>](https://github.com/vickyjaura183)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vickyjaura183 "Code") | [<img src="https://avatars.githubusercontent.com/u/32363424?v=4" width="110px;"/><br /><sub>Peace</sub>](https://github.com/julian-piehl)<br />[💻](https://github.com/snipe/snipe-it/commits?author=julian-piehl "Code") |
| [<img src="https://avatars.githubusercontent.com/u/231528?v=4" width="110px;"/><br /><sub>Kyle Gordon</sub>](https://github.com/kylegordon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kylegordon "Code") | [<img src="https://avatars.githubusercontent.com/u/53009155?v=4" width="110px;"/><br /><sub>Katharina Drexel</sub>](http://www.bfh.ch)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sunflowerbofh "Code") | [<img src="https://avatars.githubusercontent.com/u/1931963?v=4" width="110px;"/><br /><sub>David Sferruzza</sub>](https://david.sferruzza.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dsferruzza "Code") | [<img src="https://avatars.githubusercontent.com/u/19511639?v=4" width="110px;"/><br /><sub>Rick Nelson</sub>](https://github.com/rnelsonee)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rnelsonee "Code") | [<img src="https://avatars.githubusercontent.com/u/94169344?v=4" width="110px;"/><br /><sub>BasO12</sub>](https://github.com/BasO12)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BasO12 "Code") | [<img src="https://avatars.githubusercontent.com/u/111710123?v=4" width="110px;"/><br /><sub>Vautia</sub>](https://github.com/Vautia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Vautia "Code") | [<img src="https://avatars.githubusercontent.com/u/28321?v=4" width="110px;"/><br /><sub>Chris Hartjes</sub>](http://www.littlehart.net/atthekeyboard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chartjes "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2404584?v=4" width="110px;"/><br /><sub>geo-chen</sub>](https://github.com/geo-chen)<br />[💻](https://github.com/snipe/snipe-it/commits?author=geo-chen "Code") | [<img src="https://avatars.githubusercontent.com/u/6006620?v=4" width="110px;"/><br /><sub>Phan Nguyen</sub>](https://github.com/nh314)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nh314 "Code") | [<img src="https://avatars.githubusercontent.com/u/115993812?v=4" width="110px;"/><br /><sub>Iisakki Jaakkola</sub>](https://github.com/StarlessNights)<br />[💻](https://github.com/snipe/snipe-it/commits?author=StarlessNights "Code") | [<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="110px;"/><br /><sub>Ikko Ashimine</sub>](https://bandism.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=eltociear "Code") | [<img src="https://avatars.githubusercontent.com/u/56871540?v=4" width="110px;"/><br /><sub>Lukas Fehling</sub>](https://github.com/lukasfehling)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukasfehling "Code") | [<img src="https://avatars.githubusercontent.com/u/1975990?v=4" width="110px;"/><br /><sub>Fernando Almeida</sub>](https://github.com/fernando-almeida)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fernando-almeida "Code") | [<img src="https://avatars.githubusercontent.com/u/116301219?v=4" width="110px;"/><br /><sub>akemidx</sub>](https://github.com/akemidx)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akemidx "Code") |
| [<img src="https://avatars.githubusercontent.com/u/144778?v=4" width="110px;"/><br /><sub>Oguz Bilgic</sub>](http://oguz.site)<br />[💻](https://github.com/snipe/snipe-it/commits?author=oguzbilgic "Code") | [<img src="https://avatars.githubusercontent.com/u/9262438?v=4" width="110px;"/><br /><sub>Scooter Crawford</sub>](https://github.com/scoo73r)<br />[💻](https://github.com/snipe/snipe-it/commits?author=scoo73r "Code") | [<img src="https://avatars.githubusercontent.com/u/5957345?v=4" width="110px;"/><br /><sub>subdriven</sub>](https://github.com/subdriven)<br />[💻](https://github.com/snipe/snipe-it/commits?author=subdriven "Code") | [<img src="https://avatars.githubusercontent.com/u/658865?v=4" width="110px;"/><br /><sub>Andrew Savinykh</sub>](https://github.com/AndrewSav)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AndrewSav "Code") | [<img src="https://avatars.githubusercontent.com/u/1155067?v=4" width="110px;"/><br /><sub>Tadayuki Onishi</sub>](https://kenchan0130.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=kenchan0130 "Code") | [<img src="https://avatars.githubusercontent.com/u/112496896?v=4" width="110px;"/><br /><sub>Florian</sub>](https://github.com/floschoepfer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=floschoepfer "Code") | [<img src="https://avatars.githubusercontent.com/u/7305753?v=4" width="110px;"/><br /><sub>Spencer Long</sub>](http://spencerlong.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=spencerrlongg "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1141514?v=4" width="110px;"/><br /><sub>Marcus Moore</sub>](https://github.com/marcusmoore)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcusmoore "Code") | [<img src="https://avatars.githubusercontent.com/u/570639?v=4" width="110px;"/><br /><sub>Martin Meredith</sub>](https://github.com/Mezzle)<br /> | [<img src="https://avatars.githubusercontent.com/u/5731963?v=4" width="110px;"/><br /><sub>dboth</sub>](http://dboth.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dboth "Code") | [<img src="https://avatars.githubusercontent.com/u/87536651?v=4" width="110px;"/><br /><sub>Zachary Fleck</sub>](https://github.com/zacharyfleck)<br />[💻](https://github.com/snipe/snipe-it/commits?author=zacharyfleck "Code") | [<img src="https://avatars.githubusercontent.com/u/74609912?v=4" width="110px;"/><br /><sub>VIKAAS-A</sub>](https://github.com/vikaas-cyper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vikaas-cyper "Code") | [<img src="https://avatars.githubusercontent.com/u/88882041?v=4" width="110px;"/><br /><sub>Abdul Kareem</sub>](https://github.com/ak-piracha)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ak-piracha "Code") | [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") |
| [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") | [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") |
| [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") | [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") | [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") |
| [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") | [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") | [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") |
| [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") | [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/16699443?v=4" width="110px;"/><br /><sub>fvollmer</sub>](https://github.com/fvollmer)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fvollmer "Code") | [<img src="https://avatars.githubusercontent.com/u/109086466?v=4" width="110px;"/><br /><sub>36864</sub>](https://github.com/36864)<br />[💻](https://github.com/snipe/snipe-it/commits?author=36864 "Code") | [<img src="https://avatars.githubusercontent.com/u/365751?v=4" width="110px;"/><br /><sub>Daniel O'Connor</sub>](http://clockwerx.blogspot.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CloCkWeRX "Code") | [<img src="https://avatars.githubusercontent.com/u/102852568?v=4" width="110px;"/><br /><sub>BeatSpark</sub>](https://github.com/BeatSpark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=BeatSpark "Code") | [<img src="https://avatars.githubusercontent.com/u/59203607?v=4" width="110px;"/><br /><sub>mrdahbi</sub>](https://github.com/mrdahbi)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mrdahbi "Code") | [<img src="https://avatars.githubusercontent.com/u/6661332?v=4" width="110px;"/><br /><sub>Fabian Schmid</sub>](http://sr.solutions)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chfsx "Code") | [<img src="https://avatars.githubusercontent.com/u/1288116?v=4" width="110px;"/><br /><sub>Chris Olin</sub>](https://www.chrisolin.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=realchrisolin "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3803132?v=4" width="110px;"/><br /><sub>Dan</sub>](https://github.com/mnemonicly)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mnemonicly "Code") | [<img src="https://avatars.githubusercontent.com/u/43917728?v=4" width="110px;"/><br /><sub>Nebel</sub>](https://github.com/NebelKreis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NebelKreis "Code") | [<img src="https://avatars.githubusercontent.com/u/132433803?v=4" width="110px;"/><br /><sub>test1337ahp</sub>](https://github.com/test1337ahp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=test1337ahp "Code") | [<img src="https://avatars.githubusercontent.com/u/1916566?v=4" width="110px;"/><br /><sub>Jonathon Reinhart</sub>](https://github.com/JonathonReinhart)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JonathonReinhart "Code") | [<img src="https://avatars.githubusercontent.com/u/484742?v=4" width="110px;"/><br /><sub>aranar-pro</sub>](https://github.com/aranar-pro)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aranar-pro "Code") | [<img src="https://avatars.githubusercontent.com/u/27019397?v=4" width="110px;"/><br /><sub>Phil</sub>](https://github.com/phil-flip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=phil-flip "Code") | [<img src="https://avatars.githubusercontent.com/u/6473460?v=4" width="110px;"/><br /><sub>Steffy Fort</sub>](https://fe80.fr/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fe80 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/3302372?v=4" width="110px;"/><br /><sub>Jared Busch</sub>](https://github.com/sorvani)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sorvani "Code") | [<img src="https://avatars.githubusercontent.com/u/111956991?v=4" width="110px;"/><br /><sub>seanborg-codethink</sub>](https://github.com/seanborg-codethink)<br />[💻](https://github.com/snipe/snipe-it/commits?author=seanborg-codethink "Code") | [<img src="https://avatars.githubusercontent.com/u/160669961?v=4" width="110px;"/><br /><sub>dkaatz</sub>](https://github.com/dkaatz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkaatz "Code") | [<img src="https://avatars.githubusercontent.com/u/827205?v=4" width="110px;"/><br /><sub>Daniel Ruf</sub>](https://threema.id/74SF7MW6?text=)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DanielRuf "Code") | [<img src="https://avatars.githubusercontent.com/u/38883201?v=4" width="110px;"/><br /><sub>ahpaleus</sub>](https://github.com/ahpaleus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ahpaleus "Code") | [<img src="https://avatars.githubusercontent.com/u/22906055?v=4" width="110px;"/><br /><sub>Anh DAO-DUY</sub>](https://github.com/mink-adao-duy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mink-adao-duy "Code") | [<img src="https://avatars.githubusercontent.com/u/4723453?v=4" width="110px;"/><br /><sub>Andres Gutierrez</sub>](https://github.com/Serdnad)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Serdnad "Code") |
| [<img src="https://avatars.githubusercontent.com/u/111083379?v=4" width="110px;"/><br /><sub>Warren White</sub>](https://github.com/wewhite)<br />[💻](https://github.com/snipe/snipe-it/commits?author=wewhite "Code") | [<img src="https://avatars.githubusercontent.com/u/2809241?v=4" width="110px;"/><br /><sub>Robin Temme</sub>](https://robintemme.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=robintemme "Code") | [<img src="https://avatars.githubusercontent.com/u/47008367?v=4" width="110px;"/><br /><sub>herroworrd</sub>](https://github.com/herroworrd)<br />[💻](https://github.com/snipe/snipe-it/commits?author=herroworrd "Code") | [<img src="https://avatars.githubusercontent.com/u/28558609?v=4" width="110px;"/><br /><sub>vicleos</sub>](https://mubiu.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=vicleos "Code") | [<img src="https://avatars.githubusercontent.com/u/1016780?v=4" width="110px;"/><br /><sub>Bob Clough</sub>](http://thinkl33t.co.uk/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=thinkl33t "Code") | [<img src="https://avatars.githubusercontent.com/u/10648463?v=4" width="110px;"/><br /><sub>Brandon Daniel Bailey</sub>](https://github.com/brandon-bailey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brandon-bailey "Code") | [<img src="https://avatars.githubusercontent.com/u/23556080?v=4" width="110px;"/><br /><sub>Marc Bartelt</sub>](https://github.com/marcquark)<br />[💻](https://github.com/snipe/snipe-it/commits?author=marcquark "Code") |
| [<img src="https://avatars.githubusercontent.com/u/18286893?v=4" width="110px;"/><br /><sub>manu-crealytics</sub>](https://github.com/manu-crealytics)<br />[💻](https://github.com/snipe/snipe-it/commits?author=manu-crealytics "Code") | [<img src="https://avatars.githubusercontent.com/u/18245993?v=4" width="110px;"/><br /><sub>Konstantin Köhring</sub>](https://www.galaxy102.de/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Galaxy102 "Code") | [<img src="https://avatars.githubusercontent.com/u/685167?v=4" width="110px;"/><br /><sub>Deloz</sub>](https://deloz.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=deloz "Code") | [<img src="https://avatars.githubusercontent.com/u/2682426?v=4" width="110px;"/><br /><sub>Martin Berg</sub>](https://github.com/mbrrg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mbrrg "Code") | [<img src="https://avatars.githubusercontent.com/u/3694534?v=4" width="110px;"/><br /><sub>Richard Schwab</sub>](https://github.com/Nothing4You)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nothing4You "Code") | [<img src="https://avatars.githubusercontent.com/u/8959676?v=4" width="110px;"/><br /><sub>Rick Heil</sub>](https://rickheil.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rickheil "Code") | [<img src="https://avatars.githubusercontent.com/u/397106?v=4" width="110px;"/><br /><sub>Ross Crawford-d'Heureuse</sub>](https://github.com/rosscdh)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rosscdh "Code") |
| [<img src="https://avatars.githubusercontent.com/u/1621107?v=4" width="110px;"/><br /><sub>Ryan McGuire</sub>](https://github.com/McG800)<br />[💻](https://github.com/snipe/snipe-it/commits?author=McG800 "Code") | [<img src="https://avatars.githubusercontent.com/u/77835667?v=4" width="110px;"/><br /><sub>SBrown2021</sub>](https://github.com/SBrown2021)<br />[💻](https://github.com/snipe/snipe-it/commits?author=SBrown2021 "Code") | [<img src="https://avatars.githubusercontent.com/u/8780913?v=4" width="110px;"/><br /><sub>Serkan</sub>](https://github.com/serkanerip)<br />[💻](https://github.com/snipe/snipe-it/commits?author=serkanerip "Code") | [<img src="https://avatars.githubusercontent.com/u/63188620?v=4" width="110px;"/><br /><sub>Shanks</sub>](https://www.yudelei.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Shankschn "Code") | [<img src="https://avatars.githubusercontent.com/u/198525698?v=4" width="110px;"/><br /><sub>cendai-mis</sub>](https://github.com/cendai-mis)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cendai-mis "Code") | [<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="110px;"/><br /><sub>Shaun McPeck</sub>](https://smcpeck.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smcpeck "Code") | [<img src="https://avatars.githubusercontent.com/u/1378836?v=4" width="110px;"/><br /><sub>Stephen</sub>](https://github.com/snazy2000)<br />[💻](https://github.com/snipe/snipe-it/commits?author=snazy2000 "Code") |
| [<img src="https://avatars.githubusercontent.com/u/4462739?v=4" width="110px;"/><br /><sub>Steven</sub>](http://nevets82.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Nevets82 "Code") | [<img src="https://avatars.githubusercontent.com/u/29017267?v=4" width="110px;"/><br /><sub>Mateus Villar</sub>](https://mateusvillar.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Mateus-Romera "Code") | [<img src="https://avatars.githubusercontent.com/u/12749393?v=4" width="110px;"/><br /><sub>Matthew Zackschewski</sub>](https://github.com/mzack5020)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mzack5020 "Code") | [<img src="https://avatars.githubusercontent.com/u/12660103?v=4" width="110px;"/><br /><sub>Matthias Frei</sub>](https://www.frei.media/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=firefrei "Code") | [<img src="https://avatars.githubusercontent.com/u/824840?v=4" width="110px;"/><br /><sub>Nenad Ticaric</sub>](https://github.com/nticaric)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nticaric "Code") | [<img src="https://avatars.githubusercontent.com/u/706439?v=4" width="110px;"/><br /><sub>Nikolay Didenko</sub>](https://github.com/Scorcher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scorcher "Code") | [<img src="https://avatars.githubusercontent.com/u/5457236?v=4" width="110px;"/><br /><sub>Nuno Maduro</sub>](https://nunomaduro.com/sponsorships)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nunomaduro "Code") |
| [<img src="https://avatars.githubusercontent.com/u/8883074?v=4" width="110px;"/><br /><sub>Oliver Walerys</sub>](https://tektikhq.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=owalerys "Code") | [<img src="https://avatars.githubusercontent.com/u/3102039?v=4" width="110px;"/><br /><sub>R. Christian McDonald</sub>](https://keybase.io/rcmcdonald91)<br />[💻](https://github.com/snipe/snipe-it/commits?author=rcmcdonald91 "Code") | [<img src="https://avatars.githubusercontent.com/u/1525581?v=4" width="110px;"/><br /><sub>nix</sub>](https://nnix.net/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nixn "Code") | [<img src="https://avatars.githubusercontent.com/u/55462380?v=4" width="110px;"/><br /><sub>octobunny</sub>](https://github.com/octobunny)<br />[💻](https://github.com/snipe/snipe-it/commits?author=octobunny "Code") | [<img src="https://avatars.githubusercontent.com/u/8558670?v=4" width="110px;"/><br /><sub>Ryan</sub>](https://github.com/sreyemnayr)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sreyemnayr "Code") | [<img src="https://avatars.githubusercontent.com/u/1501022?v=4" width="110px;"/><br /><sub>p3nj</sub>](https://benji.ltd/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=p3nj "Code") | [<img src="https://avatars.githubusercontent.com/u/6201617?v=4" width="110px;"/><br /><sub>Tim White</sub>](https://github.com/timwsuqld)<br />[💻](https://github.com/snipe/snipe-it/commits?author=timwsuqld "Code") |
| [<img src="https://avatars.githubusercontent.com/u/22473767?v=4" width="110px;"/><br /><sub>yannikp</sub>](https://github.com/yannikp)<br />[💻](https://github.com/snipe/snipe-it/commits?author=yannikp "Code") | [<img src="https://avatars.githubusercontent.com/u/20525448?v=4" width="110px;"/><br /><sub>victoria</sub>](https://github.com/viclou)<br />[💻](https://github.com/snipe/snipe-it/commits?author=viclou "Code") | [<img src="https://avatars.githubusercontent.com/u/40685314?v=4" width="110px;"/><br /><sub>Valentyn Tulub</sub>](https://github.com/valentyntu)<br />[💻](https://github.com/snipe/snipe-it/commits?author=valentyntu "Code") | [<img src="https://avatars.githubusercontent.com/u/864520?v=4" width="110px;"/><br /><sub>Wouter van Os</sub>](http://wouter0100.nl/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Wouter0100 "Code") | [<img src="https://avatars.githubusercontent.com/u/3946540?v=4" width="110px;"/><br /><sub>Wyatt Teeter</sub>](https://www.linkedin.com/in/wyatt-teeter)<br />[💻](https://github.com/snipe/snipe-it/commits?author=xWyatt "Code") | [<img src="https://avatars.githubusercontent.com/u/1596124?v=4" width="110px;"/><br /><sub>Yorick Terweijden</sub>](https://github.com/terwey)<br />[💻](https://github.com/snipe/snipe-it/commits?author=terwey "Code") | [<img src="https://avatars.githubusercontent.com/u/69298836?v=4" width="110px;"/><br /><sub>bmkalle</sub>](https://github.com/bmkalle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bmkalle "Code") |
| [<img src="https://avatars.githubusercontent.com/u/28403467?v=4" width="110px;"/><br /><sub>bricelabelle</sub>](https://github.com/bricelabelle)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bricelabelle "Code") | [<img src="https://avatars.githubusercontent.com/u/97770090?v=4" width="110px;"/><br /><sub>corydlamb</sub>](https://github.com/corydlamb)<br />[💻](https://github.com/snipe/snipe-it/commits?author=corydlamb "Code") | [<img src="https://avatars.githubusercontent.com/u/1154133?v=4" width="110px;"/><br /><sub>Diogenes S. Jesus</sub>](http://twitter.com/splash)<br />[💻](https://github.com/snipe/snipe-it/commits?author=splashx "Code") | [<img src="https://avatars.githubusercontent.com/u/5826629?v=4" width="110px;"/><br /><sub>D M</sub>](https://github.com/dkmansion)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dkmansion "Code") | [<img src="https://avatars.githubusercontent.com/u/14837699?v=4" width="110px;"/><br /><sub>Dustin B</sub>](https://github.com/Jarli01)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Jarli01 "Code") | [<img src="https://avatars.githubusercontent.com/u/348344?v=4" width="110px;"/><br /><sub>Fabian Grutschus</sub>](https://github.com/fabiang)<br />[💻](https://github.com/snipe/snipe-it/commits?author=fabiang "Code") | [<img src="https://avatars.githubusercontent.com/u/1491053?v=4" width="110px;"/><br /><sub>MelonSmasher</sub>](https://github.com/MelonSmasher)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MelonSmasher "Code") |
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -2,7 +2,7 @@ FROM ubuntu:24.04
LABEL maintainer="Brady Wetherington <bwetherington@grokability.com>"
# No need to add `apt-get clean` here, reference:
# - https://github.com/snipe/snipe-it/pull/9201
# - https://github.com/grokability/snipe-it/pull/9201
# - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
RUN export DEBIAN_FRONTEND=noninteractive; \
@@ -110,7 +110,7 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Get dependencies
USER docker
RUN composer install --no-dev --working-dir=/var/www/html
RUN COMPOSER_CACHE_DIR=/dev/null composer install --no-dev --working-dir=/var/www/html && rm -rf /var/www/html/vendor/*/*/.git
USER root
############### APPLICATION INSTALL/INIT #################

View File

@@ -70,7 +70,7 @@ COPY --from=composer /usr/bin/composer /usr/local/bin
ARG COMPOSER_ALLOW_SUPERUSER=1
RUN set -eux; \
# Download and extract snipeit tarball
curl -o snipeit.tar.gz -fL "https://github.com/snipe/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
curl -o snipeit.tar.gz -fL "https://github.com/grokability/snipe-it/archive/v$SNIPEIT_RELEASE.tar.gz"; \
tar -xzf snipeit.tar.gz --strip-components=1 -C /var/www/html/; \
rm snipeit.tar.gz; \
# Install composer php dependencies

View File

@@ -1,6 +1,6 @@
![snipe-it-by-grok](https://github.com/snipe/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
![snipe-it-by-grok](https://github.com/grokability/snipe-it/assets/197404/b515673b-c7c8-4d9a-80f5-9fa58829a602)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://app.codacy.com/gh/snipe/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/snipe/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/snipe/snipe-it/actions/workflows/tests.yml)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/804dd1beb14a41f38810ab77d64fc4fc)](https://app.codacy.com/gh/grokability/snipe-it/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Tests](https://github.com/grokability/snipe-it/actions/workflows/tests.yml/badge.svg)](https://github.com/grokability/snipe-it/actions/workflows/tests.yml)
[![All Contributors](https://img.shields.io/badge/all_contributors-331-orange.svg?style=flat-square)](#contributing) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk)
## Snipe-IT - Open Source Asset Management System
@@ -9,7 +9,7 @@ This is a FOSS project for asset management in IT Operations. Knowing who has wh
It is built on [Laravel 11](http://laravel.com).
Snipe-IT is actively developed and we [release quite frequently](https://github.com/snipe/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
Snipe-IT is actively developed and we [release quite frequently](https://github.com/grokability/snipe-it/releases). ([Check out the live demo here](https://snipeitapp.com/demo/).)
> [!TIP]
> __This is web-based software__. This means there is no executable file (aka no .exe files), and it must be run on a web server and accessed through a web browser. It runs on any Mac OSX, any flavor of Linux, as well as Windows, and we have a [Docker image](https://snipe-it.readme.io/docs/docker) available if that's what you're into.
@@ -44,7 +44,7 @@ For help using Snipe-IT, check out the [user's manual](https://snipe-it.readme.i
-----
### Bug Reports & Feature Requests
Feel free to check out the [GitHub Issues for this project](https://github.com/snipe/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
Feel free to check out the [GitHub Issues for this project](https://github.com/grokability/snipe-it/issues) to open a bug report or see what open issues you can help with. Please search through existing issues (open *and* closed) to see if your question has already been answered before opening a new issue.
> [!IMPORTANT]
> **PLEASE see the [Getting Help Guidelines](https://snipe-it.readme.io/docs/getting-help) and [Common Issues](https://snipe-it.readme.io/docs/common-issues) before opening a ticket, and be sure to complete all of the questions in the Github Issue template to help us to help you as quickly as possible.**
@@ -133,9 +133,15 @@ The ERD is available [online here](https://drawsql.app/templates/snipe-it).
Be sure to check out all of the [amazing people](CONTRIBUTORS.md) that have contributed to Snipe-IT over the years!
-----
### Star History
[![Star History Chart](https://api.star-history.com/svg?repos=grokability/snipe-it&type=Date)](https://www.star-history.com/#grokability/snipe-it&Date)
------
### Announcement List
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
To be notified of important news (such as new releases, security advisories, etc), [sign up for our list](http://eepurl.com/XyZKz). We'll never sell or give away your info, and we'll only email you when it's important.
We also usually make smaller announcements on our social accounts, our Discord, and our blog, so be sure to subscribe to those if you're looking for more granular announcements.

View File

@@ -11,6 +11,7 @@ make it impossible to backport security fixes on older versions.
| Version | Supported |
|---------| ------------------ |
| 8.x | :white_check_mark: |
| 7.x | :white_check_mark: |
| 6.x | :x: |
| 5.1.x | :x: |

2
Vagrantfile vendored
View File

@@ -1,7 +1,7 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/snipe/snipe-it/master/snipeit.sh"
SNIPEIT_SH_URL= "https://raw.githubusercontent.com/grokability/snipe-it/master/snipeit.sh"
NETWORK_BRIDGE= "en0: Wi-Fi (AirPort)"
Vagrant.configure("2") do |config|

View File

@@ -6,7 +6,7 @@
"it asset"
],
"website": "https://snipeitapp.com/",
"repository": "https://github.com/snipe/snipe-it",
"repository": "https://github.com/grokability/snipe-it",
"logo": "https://pbs.twimg.com/profile_images/976748875733020672/K-HnZCCK_400x400.jpg",
"success_url": "/setup",
"env": {

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Console\Command;
class CleanIncorrectCheckoutAcceptances extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:clean-checkout-acceptances';
/**
* The console command description.
*
* @var string
*/
protected $description = "Delete checkout acceptances for checkouts to non-users";
/**
* Execute the console command.
*/
public function handle()
{
$deletions = 0;
$skips = 0;
// This walks *every* checkoutacceptance. That's gnarly. But necessary
$this->withProgressBar(CheckoutAcceptance::all(), function ($checkoutAcceptance) use (&$deletions, &$skips) {
$item = $checkoutAcceptance->checkoutable;
$checkout_to_id = $checkoutAcceptance->assigned_to_id;
if(is_null($item)) {
$this->info("'Checkoutable' Item is null, going to next record");
return; //'false' allegedly breaks execution entirely, so 'true' maybe doesn't? hrm. just straight return maybe?
}
if(get_class($item) == LicenseSeat::class) {
$item = $item->license;
}
foreach($item->assetlog()->where('action_type','checkout')->get() as $assetlog) {
if ($assetlog->target_id == $checkout_to_id && $assetlog->target_type != User::class) {
//We have a checkout-to an ID for a non-User, which matches to an ID in the checkout_acceptances table
//now, let's compare the _times_ - are they close?
//I'm picking `created_at` over `action_date` because I'm more interested in when the actionlogs
//were _created_, not when they were alleged to have happened - those created_at times need to be within 'X' seconds of
//each other (currently 5)
if ($assetlog->created_at->diffInSeconds($checkoutAcceptance->created_at, true) <= 5) { //we're allowing for five _ish_ seconds of slop
$deletions++;
$checkoutAcceptance->forceDelete(); // HARD delete this record; it should have never been
return;
} else {
//$this->info("The two records are too far apart");
}
} else {
//$this->info("No match! checkout to id: " . $checkout_to_id." target_id: ".$assetlog->target_id." target_type: ".$assetlog->target_type);
}
}
$skips++;
});
$this->error("Final deletion count: $deletions, and skip count: $skips");
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
class DisableSAML extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:saml-disable';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This is a rescue command that can be used to turn off SAML settings in the event that you managed to lock yourself out using bad SAML settings.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->confirm("\n****************************************************\nThis will disable SAML support. You will not be able \nto login with an account that does not exist \nlocally in the Snipe-IT local database. \n****************************************************\n\nDo you wish to continue? [y|N]")) {
$setting = Setting::getSettings();
$setting->saml_enabled = 0;
if ($setting->save()) {
$this->info('SAML has been set to disabled.');
} else {
$this->info('Unable to disable SAML.');
}
} else {
$this->info('Canceled. No actions taken.');
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class FixUpAssignedTypeWithoutAssignedTo extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-type-fixup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_type but no assigned_to';
/**
* Execute the console command.
*/
public function handle()
{
DB::table('assets')->whereNotNull('assigned_type')->whereNull('assigned_to')->update(['assigned_type' => null]);
$this->info("Assets with an assigned_type but no assigned_to are fixed");
}
}

View File

@@ -2,11 +2,9 @@
namespace App\Console\Commands;
use App\Helpers\Helper;
use Illuminate\Console\Command;
use App\Models\User;
use Laravel\Passport\TokenRepository;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Support\Facades\DB;
class GeneratePersonalAccessToken extends Command
@@ -43,9 +41,8 @@ class GeneratePersonalAccessToken extends Command
*
* @return void
*/
public function __construct(TokenRepository $tokenRepository, ValidationFactory $validation)
public function __construct(TokenRepository $tokenRepository)
{
$this->validation = $validation;
$this->tokenRepository = $tokenRepository;
parent::__construct();
}
@@ -76,7 +73,7 @@ class GeneratePersonalAccessToken extends Command
} else {
$this->warn('Your API Token has been created. Be sure to copy this token now, as it will not be accessible again.');
$this->warn('Your API Token has been created. Be sure to copy this token now, as it WILL NOT be accessible again.');
if ($token = DB::table('oauth_access_tokens')->where('user_id', '=', $user->id)->where('name','=',$accessTokenName)->orderBy('created_at', 'desc')->first()) {
$this->info('API Token ID: '.$token->id);

View File

@@ -182,7 +182,7 @@ class LdapSync extends Command
// Inject location information fields
for ($i = 0; $i < $results['count']; $i++) {
$results[$i]['ldap_location_override'] = false;
$results[$i]['location_id'] = 0;
$results[$i]['location_id'] = null;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.

View File

@@ -4,7 +4,7 @@ namespace App\Console\Commands;
use App\Models\Asset;
use App\Models\CustomField;
use Schema;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Command;
@@ -66,8 +66,8 @@ class PaveIt extends Command
foreach ($custom_fields as $custom_field) {
$this->info('DROP the '.$custom_field->db_column.' column from assets as well.');
if (\Schema::hasColumn('assets', $custom_field->db_column)) {
\Schema::table('assets', function ($table) use ($custom_field) {
if (Schema::hasColumn('assets', $custom_field->db_column)) {
Schema::table('assets', function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
}
@@ -84,8 +84,8 @@ class PaveIt extends Command
}
// Leave in the demo oauth keys so we don't have to reset them every day in the demos
\DB::statement('delete from oauth_clients WHERE id > 2');
\DB::statement('delete from oauth_access_tokens WHERE id > 2');
DB::statement('delete from oauth_clients WHERE id > 2');
DB::statement('delete from oauth_access_tokens WHERE user_id > 2');
}
}

View File

@@ -249,6 +249,7 @@ class RestoreFromBackup extends Command
'storage/private_uploads/consumables',
'storage/private_uploads/eula-pdfs',
'storage/private_uploads/imports',
'storage/private_uploads/locations',
'storage/private_uploads/licenses',
'storage/private_uploads/signatures',
'storage/private_uploads/users',
@@ -289,6 +290,7 @@ class RestoreFromBackup extends Command
$interesting_files = [];
$boring_files = [];
$unsafe_files = [];
for ($i = 0; $i < $za->numFiles; $i++) {
$stat_results = $za->statIndex($i);
@@ -327,8 +329,9 @@ class RestoreFromBackup extends Command
}
}
}
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico',];
$good_extensions = config('filesystems.allowed_upload_extensions_array');
foreach (array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
@@ -338,7 +341,9 @@ class RestoreFromBackup extends Command
if ($last_pos !== false) {
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
if (!in_array($extension, $good_extensions)) {
$this->warn('Potentially unsafe file ' . $raw_path . ' is being skipped');
// gathering potentially unsafe files here to return at exit
$unsafe_files[] = $raw_path;
Log::debug('Potentially unsafe file '.$raw_path.' is being skipped');
$boring_files[] = $raw_path;
continue 2;
}
@@ -372,6 +377,7 @@ class RestoreFromBackup extends Command
if ($this->option('sanitize-guess-prefix')) {
$prefix = SQLStreamer::guess_prefix($sql_contents);
$this->line($prefix);
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitize your SQL.");
}
@@ -505,6 +511,11 @@ class RestoreFromBackup extends Command
} else {
$this->info(count($interesting_files).' files were succesfully transferred');
}
if (count($unsafe_files) > 0) {
foreach ($unsafe_files as $unsafe_file) {
$this->warn('Potentially unsafe file '.$unsafe_file.' was skipped');
}
}
foreach ($boring_files as $boring_file) {
$this->warn($boring_file.' was skipped.');
}

View File

@@ -99,8 +99,11 @@ class SendAcceptanceReminder extends Command
foreach ($no_email_list as $user) {
$rows[] = [$user['id'], $user['name']];
}
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
if (!empty($rows)) {
$this->info("The following users do not have an email address:");
$this->table($headers, $rows);
}
return 0;
}

View File

@@ -27,7 +27,7 @@ class TestLocationsFMCS extends Command
public function handle()
{
$this->info('This script checks for company ID inconsistencies if Full Multiple Company Support with scoped locations will be used.');
$this->info('This could take few moments if have a very large dataset.');
$this->info('This could take a few moments if have a very large dataset.');
$this->newLine();
// if parameter location_id is set, only test this location

View File

@@ -19,7 +19,7 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
if(Setting::getSettings()->alerts_enabled === 1) {
if(Setting::getSettings()?->alerts_enabled === 1) {
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();

View File

@@ -28,7 +28,7 @@ class CheckoutableCheckedIn
$this->checkedOutTo = $checkedOutTo;
$this->checkedInBy = $checkedInBy;
$this->note = $note;
$this->action_date = $action_date ?? date('Y-m-d');
$this->action_date = $action_date ?? date('Y-m-d H:i:s');
$this->originalValues = $originalValues;
}
}

View File

@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Log;
use Throwable;
use JsonException;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
class Handler extends ExceptionHandler
{
@@ -107,26 +108,42 @@ class Handler extends ExceptionHandler
$statusCode = $e->getStatusCode();
// API throttle requests are handled in the RouteServiceProvider configureRateLimiting() method, so we don't need to handle them here
switch ($e->getStatusCode()) {
case '404':
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode . ' endpoint not found'), 404);
case '429':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Too many requests'), 429);
case '405':
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
}
}
// This handles API validation exceptions that happen at the Form Request level, so they
// never even get to the controller where we normally nicely format JSON responses
if ($e instanceof ValidationException) {
$response = $this->invalidJson($request, $e);
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
}
}
// This is traaaaash but it handles models that are not found while using route model binding :(
// The only alternative is to set that at *each* route, which is crazypants
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
$ids = method_exists($e, 'getIds') ? $e->getIds() : [];
// This gets the MVC model name from the exception and formats in a way that's less fugly
if (in_array('bulkedit', $ids, true)) {
$error_array = session()->get('bulk_asset_errors');
return redirect()
->route('hardware.bulkedit')
->withErrors($error_array, 'bulk_asset_errors')
->withInput();
}
// This gets the MVC model name from the exception and formats in a way that's less fugly
$model_name = strtolower(implode(" ", preg_split('/(?=[A-Z])/', last(explode('\\', $e->getModel())))));
$route = str_plural(strtolower(last(explode('\\', $e->getModel())))).'.index';
@@ -143,6 +160,10 @@ class Handler extends ExceptionHandler
$route = 'maintenances.index';
} elseif ($route === 'licenseseats.index') {
$route = 'licenses.index';
} elseif ($route === 'customfields.index') {
$route = 'fields.index';
} elseif ($route === 'customfieldsets.index') {
$route = 'fields.index';
}
return redirect()
@@ -201,6 +222,7 @@ class Handler extends ExceptionHandler
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});

View File

@@ -13,6 +13,7 @@ use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\License;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
use Carbon\Carbon;
@@ -722,8 +723,8 @@ class Helper
// The check and message that the user is still using the deprecated version
$deprecations = [
'ms_teams_deprecated' => array(
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows'),
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Jan 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
'check' => !Str::contains(Setting::getSettings()->webhook_endpoint, 'workflows') && (Setting::getSettings()->webhook_selected === 'microsoft'),
'message' => 'The Microsoft Teams webhook URL being used will be deprecated Dec 31st, 2025. <a class="btn btn-primary" href="' . route('settings.slack.index') . '">Change webhook endpoint</a>'),
];
// if item of concern is being used and its being used with the deprecated values return the notification array.
@@ -876,6 +877,48 @@ class Helper
return false;
}
/**
* Check if the file is a video, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [B. Wetherington] [<bwetherington@grokability.com>]
* @since [v8.1.18]
*/
public static function checkUploadIsVideo($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'video/mp4') || ($filetype == 'video/quicktime') || ($filetype == 'video/mpeg') || ($filetype == 'video/ogg') || ($filetype == 'video/webm') || ($filetype == 'video/x-msvide')) {
return $filetype;
}
return false;
}
/**
* Check if the file is audio, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*/
public static function checkUploadIsAudio($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'audio/mpeg') || ($filetype == 'audio/ogg')) {
return $filetype;
}
return false;
}
/**
* Walks through the permissions in the permissions config file and determines if
* permissions are granted based on a $selected_arr array.
@@ -896,6 +939,12 @@ class Helper
public static function selectedPermissionsArray($permissions, $selected_arr = [])
{
$permissions_arr = [];
if (is_array($permissions)) {
$permissions = json_encode($permissions);
}
// Set default to empty JSON if the value is null
$permissions = json_decode($permissions ?? '{}', JSON_OBJECT_AS_ARRAY);
foreach ($permissions as $permission) {
for ($x = 0; $x < count($permission); $x++) {
@@ -906,13 +955,13 @@ class Helper
if (is_array($selected_arr)) {
if (array_key_exists($permission_name, $selected_arr)) {
$permissions_arr[$permission_name] = $selected_arr[$permission_name];
$permissions_arr[$permission_name] = (int) $selected_arr[$permission_name];
} else {
$permissions_arr[$permission_name] = '0';
$permissions_arr[$permission_name] = 0;
}
} else {
$permissions_arr[$permission_name] = '0';
$permissions_arr[$permission_name] = 0;
}
}
}
@@ -1164,6 +1213,15 @@ class Helper
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
// video
'mov' => 'fa-solid fa-video',
'mp4' => 'fa-solid fa-video',
// audio
'ogg' => 'fa-solid fa-file-audio',
'mp3' => 'fa-solid fa-file-audio',
'wav' => 'fa-solid fa-file-audio',
];
if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
@@ -1307,25 +1365,24 @@ class Helper
switch ($item) {
case 'asset':
return 'fas fa-barcode';
break;
case 'accessory':
return 'fas fa-keyboard';
break;
case 'component':
return 'fas fa-hdd';
break;
case 'consumable':
return 'fas fa-tint';
break;
case 'license':
return 'far fa-save';
break;
case 'location':
return 'fas fa-map-marker-alt';
break;
case 'user':
return 'fas fa-user';
break;
case 'supplier':
return 'fa-solid fa-store';
case 'manufacturer':
return 'fa-solid fa-building';
case 'category':
return 'fa-solid fa-table-columns';
}
}
@@ -1475,59 +1532,67 @@ class Helper
}
static public function getRedirectOption($request, $id, $table, $item_id = null)
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
{
$redirect_option = Session::get('redirect_option');
$checkout_to_type = Session::get('checkout_to_type');
$redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
$checkout_to_type = Session::get('checkout_to_type') ?? null;
$checkedInFrom = Session::get('checkedInFrom');
$other_redirect = Session::get('other_redirect');
$backUrl = Session::pull('back_url', route('home'));
// return to previous page
if ($redirect_option === 'back') {
if ($backUrl === route('home')) {
return redirect()->to($backUrl)
->with('warning', trans('general.page_error'));
}
return redirect()->to($backUrl);
}
// return to index
if ($redirect_option == 'index') {
switch ($table) {
case "Assets":
return route('hardware.index');
case "Users":
return route('users.index');
case "Licenses":
return route('licenses.index');
case "Accessories":
return route('accessories.index');
case "Components":
return route('components.index');
case "Consumables":
return route('consumables.index');
}
return match ($table) {
'Assets' => redirect()->route('hardware.index'),
'Users' => redirect()->route('users.index'),
'Licenses' => redirect()->route('licenses.index'),
'Accessories' => redirect()->route('accessories.index'),
'Components' => redirect()->route('components.index'),
'Consumables' => redirect()->route('consumables.index'),
};
}
// return to thing being assigned
if ($redirect_option == 'item') {
switch ($table) {
case "Assets":
return route('hardware.show', $id ?? $item_id);
case "Users":
return route('users.show', $id ?? $item_id);
case "Licenses":
return route('licenses.show', $id ?? $item_id);
case "Accessories":
return route('accessories.show', $id ?? $item_id);
case "Components":
return route('components.show', $id ?? $item_id);
case "Consumables":
return route('consumables.show', $id ?? $item_id);
}
return match ($table) {
'Assets' => redirect()->route('hardware.show', $id ?? $item_id),
'Users' => redirect()->route('users.show', $id ?? $item_id),
'Licenses' => redirect()->route('licenses.show', $id ?? $item_id),
'Accessories' => redirect()->route('accessories.show', $id ?? $item_id),
'Components' => redirect()->route('components.show', $id ?? $item_id),
'Consumables' => redirect()->route('consumables.show', $id ?? $item_id),
};
}
// return to assignment target
if ($redirect_option == 'target') {
switch ($checkout_to_type) {
case 'user':
return route('users.show', $request->assigned_user);
case 'location':
return route('locations.show', $request->assigned_location);
case 'asset':
return route('hardware.show', $request->assigned_asset);
}
return match ($checkout_to_type) {
'user' => redirect()->route('users.show', $request->assigned_user ?? $checkedInFrom),
'location' => redirect()->route('locations.show', $request->assigned_location ?? $checkedInFrom),
'asset' => redirect()->route('hardware.show', $request->assigned_asset ?? $checkedInFrom),
};
}
// return to somewhere else
if ($redirect_option == 'other_redirect') {
return match ($other_redirect) {
'audit' => redirect()->route('assets.audit.due'),
'model' => redirect()->route('models.show', $request->model_id),
};
}
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
}
@@ -1555,6 +1620,11 @@ class Helper
$locations = Location::all();
}
// Bail out early if there are no locations
if ($locations->count() == 0) {
return [];
}
foreach($locations as $location) {
// in case of an update of a single location, use the newly requested company_id
if ($new_company_id) {
@@ -1593,14 +1663,17 @@ class Helper
$items = collect([])->push($location->$keyword);
}
$count = 0;
foreach ($items as $item) {
if ($item && $item->company_id != $location_company) {
$mismatched[] = [
class_basename(get_class($item)),
$item->id,
$item->name ?? $item->asset_tag ?? $item->serial ?? $item->username,
str_replace('App\\Models\\', '', $item->assigned_type) ?? null,
$item->assigned_type ? str_replace('App\\Models\\', '', $item->assigned_type) : null,
$item->company_id ?? null,
$item->company->name ?? null,
// $item->defaultLoc->id ?? null,
@@ -1612,6 +1685,15 @@ class Helper
$location_company ?? null,
];
$count++;
// Bail early if this is not being run via artisan
if ((!$artisan) && ($count > 0)) {
return $mismatched;
}
}
}
}

View File

@@ -16,14 +16,14 @@ class StorageHelper
$disk = config('filesystems.default');
}
switch (config("filesystems.disks.$disk.driver")) {
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 'local':
return response()->download(Storage::disk($disk)->path($filename)); //works for PRIVATE or public?!
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
case 's3':
return redirect()->away(Storage::disk($disk)->temporaryUrl($filename, now()->addMinutes(5))); //works for private or public, I guess?
default:
return Storage::disk($disk)->download($filename);
default:
return Storage::disk($disk)->download($filename);
}
}
@@ -33,21 +33,29 @@ class StorageHelper
* to determine that they are safe to display inline.
*
* @author <A. Gianotto> [<snipe@snipe.net]>
* @since v7.0.14
* @param $file_with_path
* @since v7.0.14
* @param $file_with_path
* @return bool
*/
public static function allowSafeInline($file_with_path) {
public static function allowSafeInline($file_with_path)
{
$allowed_inline = [
'pdf',
'svg',
'jpg',
'gif',
'svg',
'avif',
'webp',
'gif',
'gif',
'jpg',
'mov',
'mp3',
'mp4',
'ogg',
'pdf',
'png',
'svg',
'svg',
'wav',
'webm',
'webp',
];
@@ -59,10 +67,24 @@ class StorageHelper
}
public static function getFiletype($file_with_path)
{
// The file exists and is allowed to be displayed inline
if (Storage::exists($file_with_path)) {
return pathinfo($file_with_path, PATHINFO_EXTENSION);
}
return null;
}
/**
* Decide whether to show the file inline or download it.
*/
public static function showOrDownloadFile($file, $filename) {
public static function showOrDownloadFile($file, $filename)
{
$headers = [];

View File

@@ -83,7 +83,8 @@ class AccessoriesController extends Controller
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -167,7 +168,8 @@ class AccessoriesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.update.success'));
}
} else {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
@@ -184,15 +186,15 @@ class AccessoriesController extends Controller
*/
public function destroy($accessoryId) : RedirectResponse
{
if (is_null($accessory = Accessory::find($accessoryId))) {
if (is_null($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
$this->authorize($accessory);
if ($accessory->hasUsers() > 0) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()]));
if ($accessory->checkouts_count > 0) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/general.delete_disabled'));
}
if ($accessory->image) {
@@ -220,7 +222,10 @@ class AccessoriesController extends Controller
*/
public function show(Accessory $accessory) : View | RedirectResponse
{
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessory->id);
$accessory->loadCount('checkouts as checkouts_count');
$accessory->load(['adminuser' => fn($query) => $query->withTrashed()]);
$this->authorize('view', $accessory);
return view('accessories.view', compact('accessory'));
}

View File

@@ -7,7 +7,6 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use \Illuminate\Contracts\View\View;
@@ -30,9 +29,17 @@ class AccessoryCheckinController extends Controller
}
$accessory = Accessory::find($accessory_user->accessory_id);
//based on what the accessory is checked out to the target redirect option will be displayed accordingly.
$target_option = match ($accessory_user->assigned_type) {
'App\Models\Asset' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset')]),
'App\Models\Location' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.location')]),
default => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.user')]),
};
$this->authorize('checkin', $accessory);
return view('accessories/checkin', compact('accessory'))->with('backto', $backto);
return view('accessories/checkin', compact('accessory', 'target_option'))->with('backto', $backto);
}
/**
@@ -51,8 +58,14 @@ class AccessoryCheckinController extends Controller
$accessory = Accessory::find($accessory_checkout->accessory_id);
$this->authorize('checkin', $accessory);
session()->put('checkedInFrom', $accessory_checkout->assigned_to);
session()->put('checkout_to_type', match ($accessory_checkout->assigned_type) {
'App\Models\User' => 'user',
'App\Models\Location' => 'location',
'App\Models\Asset' => 'asset',
});
$this->authorize('checkin', $accessory);
$checkin_hours = date('H:i:s');
$checkin_at = date('Y-m-d H:i:s');
if ($request->filled('checkin_at')) {
@@ -65,7 +78,8 @@ class AccessoryCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkin.success'));
}
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));

View File

@@ -97,7 +97,7 @@ class AccessoryCheckoutController extends Controller
// Redirect to the new accessory page
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkout.success'));
}
}

View File

@@ -7,6 +7,7 @@ use App\Events\CheckoutDeclined;
use App\Events\ItemAccepted;
use App\Events\ItemDeclined;
use App\Http\Controllers\Controller;
use App\Mail\CheckoutAcceptanceResponseMail;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
@@ -20,9 +21,12 @@ use App\Models\License;
use App\Models\Component;
use App\Models\Consumable;
use App\Notifications\AcceptanceAssetAcceptedNotification;
use App\Notifications\AcceptanceAssetAcceptedToUserNotification;
use App\Notifications\AcceptanceAssetDeclinedNotification;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Http\Controllers\SettingsController;
@@ -148,6 +152,8 @@ class AcceptanceController extends Controller
}
}
$assigned_user = User::find($acceptance->assigned_to_id);
// this is horrible
switch($acceptance->checkoutable_type){
case 'App\Models\Asset':
@@ -157,35 +163,30 @@ class AcceptanceController extends Controller
return redirect()->back()->with('error', trans('admin/models/message.does_not_exist'));
}
$display_model = $asset_model->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Accessory':
$pdf_view_route ='account.accept.accept-accessory-eula';
$accessory = Accessory::find($item->id);
$display_model = $accessory->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\LicenseSeat':
$pdf_view_route ='account.accept.accept-license-eula';
$license = License::find($item->license_id);
$display_model = $license->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Component':
$pdf_view_route ='account.accept.accept-component-eula';
$component = Component::find($item->id);
$display_model = $component->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
case 'App\Models\Consumable':
$pdf_view_route ='account.accept.accept-consumable-eula';
$consumable = Consumable::find($item->id);
$display_model = $consumable->name;
$assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName;
break;
}
// if ($acceptance->checkoutable_type == 'App\Models\Asset') {
@@ -226,7 +227,7 @@ class AcceptanceController extends Controller
'note' => $request->input('note'),
'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,
'assigned_to' => $assigned_user->present()->fullName,
'company_name' => $branding_settings->site_name,
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => $path_logo,
@@ -240,6 +241,19 @@ class AcceptanceController extends Controller
}
$acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note'));
// Send the PDF to the signing user
if (($request->input('send_copy') == '1') && ($assigned_user->email !='')) {
// Add the attachment for the signing user into the $data array
$data['file'] = $pdf_filename;
try {
$assigned_user->notify(new AcceptanceAssetAcceptedToUserNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}
}
try {
$acceptance->notify(new AcceptanceAssetAcceptedNotification($data));
} catch (\Exception $e) {
@@ -337,6 +351,21 @@ class AcceptanceController extends Controller
$return_msg = trans('admin/users/message.declined');
}
if ($acceptance->alert_on_response_id) {
try {
$recipient = User::find($acceptance->alert_on_response_id);
if ($recipient) {
Mail::to($recipient)->send(new CheckoutAcceptanceResponseMail(
$acceptance,
$recipient,
$request->input('asset_acceptance') === 'accepted',
));
}
} catch (Exception $e) {
Log::warning($e);
}
}
return redirect()->to('account/accept')->with('success', $return_msg);

View File

@@ -66,7 +66,7 @@ class AccessoriesController extends Controller
}
if ($request->filled('company_id')) {
$accessories->where('company_id', '=', $request->input('company_id'));
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
}
if ($request->filled('category_id')) {
@@ -249,11 +249,11 @@ class AccessoriesController extends Controller
public function destroy($id)
{
$this->authorize('delete', Accessory::class);
$accessory = Accessory::findOrFail($id);
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
$this->authorize($accessory);
if ($accessory->hasUsers() > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()])));
if ($accessory->checkouts_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/general.delete_disabled')));
}
$accessory->delete();

View File

@@ -1,200 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function store(UploadFileRequest $request, $assetId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $asset);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assets')) {
Storage::makeDirectory('private_uploads/assets', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assets/','hardware-'.$asset->id, $file);
$asset->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function list($assetId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
// Check that there are some uploads on this asset that can be listed
if ($asset->uploads->count() > 0) {
$files = array();
foreach ($asset->uploads as $upload) {
array_push($files, $upload);
}
// Give the list of files back to the user
return response()->json(Helper::formatStandardApiResponse('success', $files, trans('admin/hardware/message.upload.success')));
}
// There are no files.
return response()->json(Helper::formatStandardApiResponse('success', array(), trans('admin/hardware/message.upload.success')));
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error')), 500);
}
/**
* Check for permissions and display the file.
*
* @param int $assetId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function show($assetId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($asset->id)) {
$this->authorize('view', $asset);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $asset->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assets/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetId
* @param int $fileId
* @since [v6.0]
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
public function destroy($assetId = null, $fileId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $asset = Asset::find($assetId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 404);
}
$rel_path = 'private_uploads/assets';
// the asset is valid
if (isset($asset->id)) {
$this->authorize('update', $asset);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.deletefile.error')), 500);
}
}

View File

@@ -75,6 +75,7 @@ class AssetMaintenancesController extends Controller
'serial',
'created_by',
'supplier',
'location',
'is_warranty',
'status_label',
];
@@ -98,6 +99,9 @@ class AssetMaintenancesController extends Controller
case 'serial':
$maintenances = $maintenances->OrderByAssetSerial($order);
break;
case 'location':
$maintenances = $maintenances->OrderLocationName($order);
break;
case 'status_label':
$maintenances = $maintenances->OrderStatusName($order);
break;

View File

@@ -1,179 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\StorageHelper;
use Illuminate\Support\Facades\Storage;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\AssetModel;
use App\Models\Actionlog;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\AssetModelsTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This class controls file related actions related
* to assets for the Snipe-IT Asset Management application.
*
* Based on the Assets/AssetFilesController by A. Gianotto <snipe@snipe.net>
*
* @version v1.0
* @author [T. Scarsbrook] [<snipe@scarzybrook.co.uk>]
*/
class AssetModelFilesController extends Controller
{
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param int $assetModelId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function store(UploadFileRequest $request, $assetModelId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// Make sure we are allowed to update this asset
$this->authorize('update', $assetModel);
if ($request->hasFile('file')) {
// If the file storage directory doesn't exist; create it
if (! Storage::exists('private_uploads/assetmodels')) {
Storage::makeDirectory('private_uploads/assetmodels', 775);
}
// Loop over the attached files and add them to the asset
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/assetmodels/','model-'.$assetModel->id, $file);
$assetModel->logUpload($file_name, e($request->get('notes')));
}
// All done - report success
return response()->json(Helper::formatStandardApiResponse('success', $assetModel, trans('admin/models/message.upload.success')));
}
// We only reach here if no files were included in the POST, so tell the user this
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.upload.nofiles')), 500);
}
/**
* List the files for an asset.
*
* @param int $assetmodel
* @since [v7.0.12]
* @author [r-xyz]
*/
public function list($assetmodel_id) : JsonResponse | array
{
$assetmodel = AssetModel::with('uploads')->find($assetmodel_id);
$this->authorize('view', $assetmodel);
return (new AssetModelsTransformer)->transformAssetModelFiles($assetmodel, $assetmodel->uploads()->count());
}
/**
* Check for permissions and display the file.
*
* @param int $assetModelId
* @param int $fileId
* @return \Illuminate\Http\JsonResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v7.0.12]
* @author [r-xyz]
*/
public function show($assetModelId = null, $fileId = null) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('view', $assetModel);
// Check that the file being requested exists for the asset
if (! $log = Actionlog::whereNotNull('filename')->where('item_id', $assetModel->id)->find($fileId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.no_match', ['id' => $fileId])), 404);
}
// Form the full filename with path
$file = 'private_uploads/assetmodels/'.$log->filename;
Log::debug('Checking for '.$file);
if ($log->action_type == 'audit') {
$file = 'private_uploads/audits/'.$log->filename;
}
// Check the file actually exists on the filesystem
if (! Storage::exists($file)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.does_not_exist', ['id' => $fileId])), 404);
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
// Send back an error message
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.download.error', ['id' => $fileId])), 500);
}
/**
* Delete the associated file
*
* @param int $assetModelId
* @param int $fileId
* @since [v7.0.12]
* @author [r-xyz]
*/
public function destroy($assetModelId = null, $fileId = null) : JsonResponse
{
// Start by checking if the asset being acted upon exists
if (! $assetModel = AssetModel::find($assetModelId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.does_not_exist')), 404);
}
$rel_path = 'private_uploads/assetmodels';
// the asset is valid
if (isset($assetModel->id)) {
$this->authorize('update', $assetModel);
// Check for the file
$log = Actionlog::find($fileId);
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists($rel_path.'/'.$log->filename)) {
Storage::delete($rel_path.'/'.$log->filename);
}
// Delete the record of the file
$log->delete();
// All deleting done - notify the user of success
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/models/message.deletefile.success')), 200);
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/models/message.deletefile.error')), 500);
}
}

View File

@@ -85,6 +85,12 @@ class AssetModelsController extends Controller
$assetmodels = $assetmodels->where('models.model_number', '=', $request->input('model_number'));
}
if ($request->input('requestable') == 'true') {
$assetmodels = $assetmodels->where('models.requestable', '=', '1');
} elseif ($request->input('requestable') == 'false') {
$assetmodels = $assetmodels->where('models.requestable', '=', '0');
}
if ($request->filled('notes')) {
$assetmodels = $assetmodels->where('models.notes', '=', $request->input('notes'));
}
@@ -148,7 +154,7 @@ class AssetModelsController extends Controller
$assetmodel = $request->handleImages($assetmodel);
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));
@@ -201,7 +207,7 @@ class AssetModelsController extends Controller
$assetmodel = AssetModel::findOrFail($id);
$assetmodel->fill($request->all());
$assetmodel = $request->handleImages($assetmodel);
/**
* Allow custom_fieldset_id to override and populate fieldset_id.
* This is stupid, but required for legacy API support.
@@ -216,7 +222,7 @@ class AssetModelsController extends Controller
if ($assetmodel->save()) {
return response()->json(Helper::formatStandardApiResponse('success', $assetmodel, trans('admin/models/message.update.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetModelsTransformer)->transformAssetModel($assetmodel), trans('admin/models/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $assetmodel->getErrors()));

View File

@@ -114,6 +114,7 @@ class AssetsController extends Controller
'byod',
'asset_eol_date',
'requestable',
'jobtitle',
];
$filter = [];
@@ -298,9 +299,15 @@ class AssetsController extends Controller
if ($request->input('requestable') == 'true') {
$assets->where('assets.requestable', '=', '1');
}
if ($request->filled('model_id')) {
$assets->InModelList([$request->input('model_id')]);
// If model_id is already an array, just use it as-is
if (is_array($request->input('model_id'))) {
$assets->InModelList($request->input('model_id'));
} else {
// Otherwise, turn it into an array
$assets->InModelList([$request->input('model_id')]);
}
}
if ($request->filled('category_id')) {
@@ -389,6 +396,9 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
case 'jobtitle':
$assets->OrderByJobTitle($order);
break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;
@@ -436,12 +446,6 @@ class AssetsController extends Controller
}]);
}
/**
* Here we're just determining which Transformer (via $transformer) to use based on the
* variables we set earlier on in this method - we default to AssetsTransformer.
*/
return (new $transformer)->transformAssets($assets, $total, $request);
}
@@ -574,7 +578,12 @@ class AssetsController extends Controller
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id',
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
])->with('model', 'assetstatus', 'assignedTo')
->NotArchived();
if ((Setting::getSettings()->full_multiple_companies_support=='1') && ($request->filled('companyId'))) {
$assets->where('assets.company_id', $request->input('companyId'));
}
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
@@ -584,7 +593,6 @@ class AssetsController extends Controller
$assets = $assets->AssignedSearch($request->input('search'));
}
$assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use.
@@ -697,7 +705,9 @@ class AssetsController extends Controller
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
// below is what we want the _eventual_ return to look like - in a more standardized format.
// return response()->json(Helper::formatStandardApiResponse('success', (new AssetsTransformer)->transformAsset($asset), trans('admin/hardware/message.create.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
@@ -1135,12 +1145,12 @@ class AssetsController extends Controller
}
}
// Validate custom fields
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
// Validate the rest of the data before we turn off the event dispatcher
if ($asset->isInvalid()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag' => $asset->asset_tag], $asset->getErrors()));
}

View File

@@ -56,7 +56,7 @@ class CategoriesController extends Controller
'notes',
])
->with('adminuser')
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count');
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
/*
@@ -212,7 +212,7 @@ class CategoriesController extends Controller
public function destroy($id) : JsonResponse
{
$this->authorize('delete', Category::class);
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count')->findOrFail($id);
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
if (! $category->isDeletable()) {
return response()->json(

View File

@@ -43,7 +43,10 @@ class CompaniesController extends Controller
$companies = Company::withCount(['assets as assets_count' => function ($query) {
$query->AssetsForShow();
}])->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
}])
->with('adminuser')
->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
if ($request->filled('search')) {
$companies->TextSearch($request->input('search'));
@@ -119,6 +122,7 @@ class CompaniesController extends Controller
{
$this->authorize('view', Company::class);
$company = Company::findOrFail($id);
$this->authorize('view', $company);
return (new CompaniesTransformer)->transformCompany($company);
}
@@ -136,6 +140,7 @@ class CompaniesController extends Controller
{
$this->authorize('update', Company::class);
$company = Company::findOrFail($id);
$this->authorize('update', $company);
$company->fill($request->all());
$company = $request->handleImages($company);
@@ -188,6 +193,7 @@ class CompaniesController extends Controller
'companies.image',
]);
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
}

View File

@@ -60,7 +60,7 @@ class ComponentsController extends Controller
}
if ($request->filled('company_id')) {
$components->where('company_id', '=', $request->input('company_id'));
$components->where('components.company_id', '=', $request->input('company_id'));
}
if ($request->filled('category_id')) {

View File

@@ -40,7 +40,7 @@ class ConsumablesController extends Controller
}
if ($request->filled('company_id')) {
$consumables->where('company_id', '=', $request->input('company_id'));
$consumables->where('consumables.company_id', '=', $request->input('company_id'));
}
if ($request->filled('category_id')) {

View File

@@ -76,7 +76,7 @@ class GroupsController extends Controller
$this->authorize('superadmin');
$group = new Group;
// Get all the available permissions
$permissions = config('permissions');
$permissions = json_encode(config('permissions'));
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
$group->name = $request->input('name');

View File

@@ -234,6 +234,15 @@ class ImportController extends Controller
case 'location':
$redirectTo = 'locations.index';
break;
case 'supplier':
$redirectTo = 'suppliers.index';
break;
case 'manufacturer':
$redirectTo = 'manufacturers.index';
break;
case 'category':
$redirectTo = 'categories.index';
break;
}
if ($errors) { //Failure

View File

@@ -29,12 +29,21 @@ class LicenseSeatsController extends Controller
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
->where('license_seats.license_id', $licenseId);
if ($request->input('status') == 'available') {
$seats->whereNull('license_seats.assigned_to');
}
if ($request->input('status') == 'assigned') {
$seats->ByAssigned();
}
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
if ($request->input('sort') == 'department') {
$seats->OrderDepartments($order);
} else {
$seats->orderBy('id', $order);
$seats->orderBy('updated_at', $order);
}
$total = $seats->count();

View File

@@ -87,7 +87,8 @@ class LocationsController extends Controller
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count');
->withCount('users as users_count')
->with('adminuser');
// Only scope locations if the setting is enabled
if (Setting::getSettings()->scope_locations_fmcs) {
@@ -218,6 +219,7 @@ class LocationsController extends Controller
'locations.updated_at',
'locations.image',
'locations.currency',
'locations.company_id',
'locations.notes',
])
->withCount('assignedAssets as assigned_assets_count')

View File

@@ -4,15 +4,19 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\ProfileTransformer;
use App\Models\CheckoutRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Laravel\Passport\TokenRepository;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Support\Facades\Gate;
use App\Models\CustomField;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ProfileController extends Controller
{
@@ -167,6 +171,22 @@ class ProfileController extends Controller
}
/**
* Display the EULAs accepted by the user.
*
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
* @return \Illuminate\Http\JsonResponse
*@since [v8.1.16]
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
*/
public function eulas(ProfileTransformer $transformer)
{
// Only return this user's EULAs
$eulas = auth()->user()->eulas;
return response()->json(
$transformer->transformFiles($eulas, $eulas->count())
);
}
}

View File

@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Transformers\ActionlogsTransformer;
use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
@@ -18,10 +20,11 @@ class ReportsController extends Controller
*/
public function index(Request $request) : JsonResponse | array
{
$this->authorize('reports.view');
$this->authorize('activity.view');
$actionlogs = Actionlog::with('item', 'user', 'adminuser', 'target', 'location');
if ($request->filled('search')) {
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
}

View File

@@ -24,10 +24,15 @@ class SuppliersController extends Controller
public function index(Request $request): array
{
$this->authorize('view', Supplier::class);
$allowed_columns = ['
id',
$allowed_columns = [
'id',
'name',
'address',
'address2',
'city',
'state',
'country',
'zip',
'phone',
'contact',
'fax',
@@ -39,21 +44,24 @@ class SuppliersController extends Controller
'components_count',
'consumables_count',
'url',
'notes',
];
$suppliers = Supplier::select(
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'updated_at', 'deleted_at', 'image', 'notes', 'url'])
['id', 'name', 'address', 'address2', 'city', 'state', 'country', 'fax', 'phone', 'email', 'contact', 'created_at', 'created_by', 'updated_at', 'deleted_at', 'image', 'notes', 'url', 'zip'])
->withCount('assets as assets_count')
->withCount('licenses as licenses_count')
->withCount('accessories as accessories_count')
->withCount('components as components_count')
->withCount('consumables as consumables_count');
->withCount('consumables as consumables_count')
->with('adminuser');
if ($request->filled('search')) {
$suppliers = $suppliers->TextSearch($request->input('search'));
$suppliers->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$suppliers->where('name', '=', $request->input('name'));
}
@@ -100,7 +108,15 @@ class SuppliersController extends Controller
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$suppliers->orderBy($sort, $order);
switch ($request->input('sort')) {
case 'created_by':
$suppliers->OrderByCreatedByName($order);
break;
default:
$suppliers->orderBy($sort, $order);
break;
}
$total = $suppliers->count();
$suppliers = $suppliers->skip($offset)->take($limit)->get();

View File

@@ -0,0 +1,259 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\UploadFileRequest;
use App\Http\Transformers\UploadedFilesTransformer;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Location;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class UploadedFilesController extends Controller
{
static $map_object_type = [
'accessories' => Accessory::class,
'assets' => Asset::class,
'components' => Component::class,
'consumables' => Consumable::class,
'hardware' => Asset::class,
'licenses' => License::class,
'locations' => Location::class,
'models' => AssetModel::class,
'users' => User::class,
];
static $map_storage_path = [
'accessories' => 'private_uploads/accessories/',
'assets' => 'private_uploads/assets/',
'components' => 'private_uploads/components/',
'consumables' => 'private_uploads/consumables/',
'hardware' => 'private_uploads/assets/',
'licenses' => 'private_uploads/licenses/',
'locations' => 'private_uploads/locations/',
'models' => 'private_uploads/assetmodels/',
'users' => 'private_uploads/users/',
];
static $map_file_prefix= [
'accessories' => 'accessory',
'assets' => 'asset',
'components' => 'component',
'consumables' => 'consumable',
'hardware' => 'asset',
'licenses' => 'license',
'locations' => 'location',
'models' => 'model',
'users' => 'user',
];
/**
* List files for an object
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to list files for
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function index(Request $request, $object_type, $id) : JsonResponse | array
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Columns allowed for sorting
$allowed_columns =
[
'id',
'filename',
'action_type',
'action_date',
'note',
'created_at',
];
$uploads = $object->uploads();
$offset = ($request->input('offset') > $object->count()) ? $object->count() : abs($request->input('offset'));
$limit = app('api_limit_value');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'action_logs.created_at';
// Text search on action_logs fields
// We could use the normal Actionlogs text scope, but it's a very heavy query since it's searcghing across all relations
// And we generally won't need that here
if ($request->filled('search')) {
$uploads->where(
function ($query) use ($request) {
$query->where('filename', 'LIKE', '%' . $request->input('search') . '%')
->orWhere('note', 'LIKE', '%' . $request->input('search') . '%');
}
);
}
$uploads = $uploads->skip($offset)->take($limit)->orderBy($sort, $order)->get();
return (new UploadedFilesTransformer())->transformFiles($uploads, $uploads->count());
}
/**
* Accepts a POST to upload a file to the server.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to store so we can check permisisons
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, $object_type, $id) : JsonResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// If the file storage directory doesn't exist, create it
if (! Storage::exists(self::$map_storage_path[$object_type])) {
Storage::makeDirectory(self::$map_storage_path[$object_type], 775);
}
if ($request->hasFile('file')) {
// Loop over the attached files and add them to the object
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
$files[] = $file_name;
$object->logUpload($file_name, $request->get('notes'));
}
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
->where('item_type', '=', self::$map_object_type[$object_type])
->where('item_id', '=', $id)->whereIn('filename', $files)
->get();
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
}
// No files were submitted
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.nofiles')));
}
/**
* Check for permissions and display the file.
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to delete from so we can check permisisons
* @param $file_id the ID of the file to delete from the action_logs table
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function show($object_type, $id, $file_id) : JsonResponse | StreamedResponse | Storage | StorageHelper | BinaryFileResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('view', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Check that the file being requested exists for the object
if (! $log = Actionlog::whereNotNull('filename')->where('item_type', self::$map_object_type[$object_type])->where('item_id', $object->id)->find($file_id)
) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_id')), 200);
}
if (! Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.file_not_found'), 200));
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download(self::$map_storage_path[$object_type].'/'.$log->filename, $log->filename, $headers);
}
return StorageHelper::downloader(self::$map_storage_path[$object_type].'/'.$log->filename);
}
/**
* Delete the associated file
*
* @param \App\Http\Requests\UploadFileRequest $request
* @param string $object_type the type of object to upload the file to
* @param int $id the ID of the object to delete from so we can check permisisons
* @param $file_id the ID of the file to delete from the action_logs table
* @since [v8.1.17]
* @author [A. Gianotto <snipe@snipe.net>]
*/
public function destroy($object_type, $id, $file_id) : JsonResponse
{
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::find($id);
$this->authorize('update', self::$map_object_type[$object_type]);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
}
// Check for the file
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
->where('item_id', $object->id)->first();
if ($log) {
// Check the file actually exists, and delete it
if (Storage::exists(self::$map_storage_path[$object_type].'/'.$log->filename)) {
Storage::delete(self::$map_storage_path[$object_type].'/'.$log->filename);
}
// Delete the record of the file
if ($log->delete()) {
return response()->json(Helper::formatStandardApiResponse('success', null, trans_choice('general.file_upload_status.delete.success', 1)), 200);
}
}
// The file doesn't seem to really exist, so report an error
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
}
}

View File

@@ -6,6 +6,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\SaveUserRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\ActionlogsTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\ConsumablesTransformer;
use App\Http\Transformers\LicensesTransformer;
@@ -22,6 +23,7 @@ use App\Notifications\CurrentInventory;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;
@@ -80,7 +82,12 @@ class UsersController extends Controller
'users.autoassign_licenses',
'users.website',
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy', 'managesUsers', 'managedLocations')
])->with('manager')
->with('groups')
->with('userloc')
->with('company')
->with('department')
->with('createdBy')
->withCount([
'assets as assets_count' => function(Builder $query) {
$query->withoutTrashed();
@@ -206,11 +213,11 @@ class UsersController extends Controller
}
if ($request->filled('manages_users_count')) {
$users->has('manages_users_count', '=', $request->input('manages_users_count'));
$users->has('managesUsers', '=', $request->input('manages_users_count'));
}
if ($request->filled('manages_locations_count')) {
$users->has('manages_locations_count', '=', $request->input('manages_locations_count'));
$users->has('managedLocations', '=', $request->input('manages_locations_count'));
}
if ($request->filled('autoassign_licenses')) {
@@ -339,6 +346,7 @@ class UsersController extends Controller
$users = $users->where(function ($query) use ($request) {
$query->SimpleNameSearch($request->get('search'))
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
->orWhere('email', 'LIKE', '%'.$request->get('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
});
}
@@ -473,8 +481,25 @@ class UsersController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, 'You cannot be your own manager'));
}
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
// check for permissions related fields and pull them out if the current user cannot edit them
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
if ($request->filled('username')) {
$user->username = $request->input('username');
}
if ($request->filled('email')) {
$user->email = $request->input('email');
}
if ($request->filled('activated')) {
$user->activated = $request->input('activated');
}
}
// We need to use has() instead of filled()
@@ -675,7 +700,6 @@ class UsersController extends Controller
$this->authorize('view', License::class);
if ($user = User::where('id', $id)->withTrashed()->first()) {
$this->authorize('update', $user);
$licenses = $user->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
@@ -735,6 +759,25 @@ class UsersController extends Controller
return (new UsersTransformer)->transformUser($request->user());
}
/**
* Display the EULAs accepted by the user.
*
* @param \App\Models\User $user
* @param \App\Http\Transformers\ActionlogsTransformer $transformer
* @return \Illuminate\Http\JsonResponse
*@since [v8.1.16]
* @author [Godfrey Martinez] [<gmartinez@grokability.com>]
*/
public function eulas(User $user, ActionlogsTransformer $transformer)
{
$this->authorize('view', User::class);
$eulas = $user->eulas;
return response()->json(
$transformer->transformActionlogs($eulas, $eulas->count())
);
}
/**
* Restore a soft-deleted user.
*

View File

@@ -19,19 +19,6 @@ use \Illuminate\Http\RedirectResponse;
*/
class AssetMaintenancesController extends Controller
{
/**
* Checks for permissions for this action.
*
* @todo This should be replaced with middleware and/or policies
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @version v1.0
* @since [v1.8]
*/
private static function getInsufficientPermissionsRedirect(): RedirectResponse
{
return redirect()->route('maintenances.index')
->with('error', trans('general.insufficient_permissions'));
}
/**
* Returns a view that invokes the ajax tables which actually contains
@@ -67,16 +54,10 @@ class AssetMaintenancesController extends Controller
// We have to set this so that the correct property is set in the select2 ajax dropdown
$asset->asset_id = $asset->id;
}
// Prepare Asset Maintenance Type List
$assetMaintenanceType = [
'' => 'Select an asset maintenance type',
] + AssetMaintenance::getImprovementOptions();
// Mark the selected asset, if it came in
return view('asset_maintenances/edit')
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('asset', $asset)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', new AssetMaintenance);
}
@@ -91,43 +72,45 @@ class AssetMaintenancesController extends Controller
public function store(Request $request) : RedirectResponse
{
$this->authorize('update', Asset::class);
// create a new model instance
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
$asset = Asset::find($request->input('asset_id'));
if ((! Company::isCurrentUserHasAccess($asset)) && ($asset != null)) {
return static::getInsufficientPermissionsRedirect();
$assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
// Loop through the selected assets
foreach ($assets as $asset) {
$assetMaintenance = new AssetMaintenance();
$assetMaintenance->supplier_id = $request->input('supplier_id');
$assetMaintenance->is_warranty = $request->input('is_warranty');
$assetMaintenance->cost = $request->input('cost');
$assetMaintenance->notes = $request->input('notes');
// Save the asset maintenance data
$assetMaintenance->asset_id = $asset->id;
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->created_by = auth()->id();
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if (!$assetMaintenance->save()) {
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
}
// Save the asset maintenance data
$assetMaintenance->asset_id = $request->input('asset_id');
$assetMaintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$assetMaintenance->title = $request->input('title');
$assetMaintenance->start_date = $request->input('start_date');
$assetMaintenance->completion_date = $request->input('completion_date');
$assetMaintenance->created_by = auth()->id();
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.create.success'));
if (($assetMaintenance->completion_date !== null)
&& ($assetMaintenance->start_date !== '')
&& ($assetMaintenance->start_date !== '0000-00-00')
) {
$startDate = Carbon::parse($assetMaintenance->start_date);
$completionDate = Carbon::parse($assetMaintenance->completion_date);
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if ($assetMaintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($assetMaintenance->getErrors());
}
/**
@@ -135,26 +118,19 @@ class AssetMaintenancesController extends Controller
*
* @see AssetMaintenancesController::postEdit() method that stores the data
* @author Vincent Sposato <vincent.sposato@gmail.com>
* @param int $assetMaintenanceId
* @version v1.0
* @since [v1.8]
*/
public function edit(AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Prepare Improvement Type List
$assetMaintenanceType = ['' => 'Select an improvement type'] + AssetMaintenance::getImprovementOptions();
$this->authorize('update', $maintenance->asset);
return view('asset_maintenances/edit')
->with('selectedAsset', null)
->with('assetMaintenanceType', $assetMaintenanceType)
->with('item', $maintenance);
->with('selected_assets', $maintenance->asset->pluck('id')->toArray())
->with('asset_ids', request()->input('asset_ids', []))
->with('assetMaintenanceType', AssetMaintenance::getImprovementOptions())
->with('item', $maintenance);
}
/**
@@ -170,33 +146,21 @@ class AssetMaintenancesController extends Controller
public function update(Request $request, AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('update', Asset::class);
if ((!$maintenance->asset) || ($maintenance->asset->deleted_at!='')) {
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
} elseif (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
$this->authorize('update', $maintenance->asset);
$maintenance->supplier_id = $request->input('supplier_id');
$maintenance->is_warranty = $request->input('is_warranty');
$maintenance->is_warranty = $request->input('is_warranty', 0);
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$asset = Asset::find(request('asset_id'));
if (! Company::isCurrentUserHasAccess($asset)) {
return static::getInsufficientPermissionsRedirect();
}
// Save the asset maintenance data
$maintenance->asset_id = $request->input('asset_id');
$maintenance->asset_maintenance_type = $request->input('asset_maintenance_type');
$maintenance->title = $request->input('title');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
if (($maintenance->completion_date == null)
) {
// Todo - put this in a getter/setter?
if (($maintenance->completion_date == null))
{
if (($maintenance->asset_maintenance_time !== 0)
|| (! is_null($maintenance->asset_maintenance_time))
) {
@@ -213,10 +177,7 @@ class AssetMaintenancesController extends Controller
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
}
// Was the asset maintenance created?
if ($maintenance->save()) {
// Redirect to the new asset maintenance page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.edit.success'));
}
@@ -232,21 +193,12 @@ class AssetMaintenancesController extends Controller
* @version v1.0
* @since [v1.8]
*/
public function destroy($assetMaintenanceId) : RedirectResponse
public function destroy(AssetMaintenance $maintenance) : RedirectResponse
{
$this->authorize('update', Asset::class);
// Check if the asset maintenance exists
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
// Redirect to the asset maintenance management page
return redirect()->route('maintenances.index')
->with('error', trans('admin/asset_maintenances/message.not_found'));
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
$this->authorize('update', $maintenance->asset);
// Delete the asset maintenance
$assetMaintenance->delete();
$maintenance->delete();
// Redirect to the asset_maintenance management page
return redirect()->route('maintenances.index')
->with('success', trans('admin/asset_maintenances/message.delete.success'));
@@ -262,11 +214,6 @@ class AssetMaintenancesController extends Controller
*/
public function show(AssetMaintenance $maintenance) : View | RedirectResponse
{
$this->authorize('view', Asset::class);
if (! Company::isCurrentUserHasAccess($maintenance->asset)) {
return static::getInsufficientPermissionsRedirect();
}
return view('asset_maintenances/view')->with('assetMaintenance', $maintenance);
}
}

View File

@@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Validator;
class AssetCheckinController extends Controller
{
@@ -41,7 +42,19 @@ class AssetCheckinController extends Controller
return redirect()->route('hardware.show', $asset->id)->with('error', trans('admin/hardware/general.model_invalid_fix'));
}
return view('hardware/checkin', compact('asset'))
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
if ($asset->isInvalid()) {
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
}
$target_option = match ($asset->assigned_type) {
'App\Models\Asset' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.asset_previous')]),
'App\Models\Location' => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.location')]),
default => trans('admin/hardware/form.redirect_to_type', ['type' => trans('general.user')]),
};
return view('hardware/checkin', compact('asset', 'target_option'))
->with('item', $asset)
->with('statusLabel_list', Helper::statusLabelList())
->with('backto', $backto)
@@ -75,12 +88,14 @@ class AssetCheckinController extends Controller
$this->authorize('checkin', $asset);
if ($asset->assignedType() == Asset::USER) {
$user = $asset->assignedTo;
}
session()->put('checkedInFrom', $asset->assignedTo->id);
session()->put('checkout_to_type', match ($asset->assigned_type) {
'App\Models\User' => 'user',
'App\Models\Location' => 'location',
'App\Models\Asset' => 'asset',
});
$asset->expected_checkin = null;
$asset->last_checkin = now();
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
$asset->name = $request->get('name');
@@ -107,11 +122,14 @@ class AssetCheckinController extends Controller
$originalValues = $asset->getRawOriginal();
// Handle last checkin date
$checkin_at = date('Y-m-d H:i:s');
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
$originalValues['action_date'] = $checkin_at;
$checkin_at = $request->get('checkin_at');
}
$asset->last_checkin = $checkin_at;
$asset->licenseseats->each(function (LicenseSeat $seat) {
$seat->update(['assigned_to' => null]);
@@ -135,7 +153,8 @@ class AssetCheckinController extends Controller
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkin.success'));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());

View File

@@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Session;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Validator;
class AssetCheckoutController extends Controller
{
@@ -36,6 +37,14 @@ class AssetCheckoutController extends Controller
->with('error', trans('admin/hardware/general.model_invalid_fix'));
}
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
if ($asset->isInvalid()) {
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
}
if ($asset->availableForCheckout()) {
return view('hardware/checkout', compact('asset'))
->with('statusLabel_list', Helper::deployableStatusLabelList())
@@ -114,7 +123,7 @@ class AssetCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error

View File

@@ -149,7 +149,7 @@ class AssetsController extends Controller
$asset->byod = request('byod', 0);
if (! empty($settings->audit_interval)) {
$asset->next_audit_date = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$asset->next_audit_date = Carbon::now()->addMonths((int) $settings->audit_interval)->toDateString();
}
// Set location_id to rtd_location_id ONLY if the asset isn't being checked out
@@ -188,14 +188,31 @@ class AssetsController extends Controller
// Validate the asset before saving
if ($asset->isValid() && $asset->save()) {
if (request('assigned_user')) {
$target = User::find(request('assigned_user'));
$target = null;
$location = null;
if ($userId = request('assigned_user')) {
$target = User::find($userId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.user'));
}
$location = $target->location_id;
} elseif (request('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
} elseif ($assetId = request('assigned_asset')) {
$target = Asset::find($assetId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.asset'));
}
$location = $target->location_id;
} elseif (request('assigned_location')) {
$target = Location::find(request('assigned_location'));
} elseif ($locationId = request('assigned_location')) {
$target = Location::find($locationId);
if (!$target) {
return redirect()->back()->withInput()->with('error', trans('admin/hardware/message.create.target_not_found.location'));
}
$location = $target->id;
}
@@ -210,24 +227,27 @@ class AssetsController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => 'model' ]);
if ($successes) {
if ($failures) {
//some succeeded, some failed
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
return Helper::getRedirectOption($request, $asset->id, 'Assets') //FIXME - not tested
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
} else {
if (count($successes) == 1) {
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
//and re-translated
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset), 'id', 'tag' => e($asset->asset_tag)]));
} else {
//multi-success
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
}
}
@@ -248,6 +268,7 @@ class AssetsController extends Controller
public function edit(Asset $asset) : View | RedirectResponse
{
$this->authorize($asset);
session()->put('back_url', url()->previous());
return view('hardware/edit')
->with('item', $asset)
->with('statuslabel_list', Helper::statusLabelList())
@@ -351,11 +372,6 @@ class AssetsController extends Controller
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update with '.$status->getStatuslabelType().' status', date('Y-m-d H:i:s'), $originalValues));
}
if ($asset->assigned_to == '') {
$asset->location_id = $request->input('rtd_location_id', null);
}
if ($request->filled('image_delete')) {
try {
unlink(public_path().'/uploads/assets/'.$asset->image);
@@ -415,11 +431,15 @@ class AssetsController extends Controller
}
}
}
session()->put([
'redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => $request->get('redirect_option') === 'other_redirect' ? 'model' : null,
]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->save()) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -451,7 +471,7 @@ class AssetsController extends Controller
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on delete', $checkin_at, $originalValues));
DB::table('assets')
->where('id', $asset->id)
->update(['assigned_to' => null]);
->update(['assigned_to' => null, 'assigned_type' => null]);
}
@@ -463,6 +483,7 @@ class AssetsController extends Controller
}
}
$asset->delete();
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
@@ -524,7 +545,7 @@ class AssetsController extends Controller
{
$settings = Setting::getSettings();
if (($settings->qr_code == '1') && ($settings->label2_2d_type !== 'none')) {
if ($settings->label2_2d_type !== 'none') {
if ($asset) {
$size = Helper::barcodeDimensions($settings->label2_2d_type);
@@ -882,11 +903,20 @@ class AssetsController extends Controller
}
public function audit(Asset $asset)
public function audit(Asset $asset): View | RedirectResponse
{
$settings = Setting::getSettings();
$this->authorize('audit', Asset::class);
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$settings = Setting::getSettings();
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
if ($asset->isInvalid()) {
return redirect()->route('hardware.edit', $asset)->withErrors($asset->getErrors());
}
$dt = Carbon::now()->addMonths( (int) $settings->audit_interval)->toDateString();
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
}
@@ -895,6 +925,10 @@ class AssetsController extends Controller
$this->authorize('audit', Asset::class);
session()->put('redirect_option', $request->get('redirect_option'));
session()->put('other_redirect', 'audit');
$originalValues = $asset->getRawOriginal();
$asset->next_audit_date = $request->input('next_audit_date');
@@ -929,8 +963,8 @@ class AssetsController extends Controller
}
}
// Validate custom fields
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
// Invoke the validation to see if the audit will complete successfully
$asset->setRules($asset->getRules() + $asset->customFieldValidationRules());
// Validate the rest of the data before we turn off the event dispatcher
if ($asset->isInvalid()) {
@@ -970,7 +1004,7 @@ class AssetsController extends Controller
}
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
return Helper::getRedirectOption($request, $asset->id, 'Assets')->with('success', trans('admin/hardware/message.audit.success'));
}
return redirect()->back()->withInput()->withErrors($asset->getErrors());

View File

@@ -52,11 +52,26 @@ class BulkAssetsController extends Controller
}
$asset_ids = $request->input('ids');
if ($request->input('bulk_actions') === 'checkout') {
$status_check =$this->hasUndeployableStatus($asset_ids);
if($status_check && $status_check['status'] === true){
$asset_tags = implode(', ', array_column($status_check['tags'], 'asset_tag'));
$asset_ids = $status_check['asset_ids'];
session()->flash('warning', trans('admin/hardware/message.undeployable', ['asset_tags' => $asset_tags]));
}
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('hardware.bulkcheckout.show');
}
if ($request->input('bulk_actions') === 'maintenance') {
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect()->route('maintenances.create');
}
// 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');
session(['bulk_back_url' => $bulk_back_url]);
@@ -97,11 +112,47 @@ class BulkAssetsController extends Controller
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
$assets = Asset::with('assignedTo', 'location', 'model')
$query = Asset::with('assignedTo', 'location', 'model')
->whereIn('assets.id', $asset_ids)
->withTrashed();
$assets = $assets->get();
switch ($sort_override) {
case 'model':
$query->OrderModels($order);
break;
case 'model_number':
$query->OrderModelNumber($order);
break;
case 'category':
$query->OrderCategory($order);
break;
case 'manufacturer':
$query->OrderManufacturer($order);
break;
case 'company':
$query->OrderCompany($order);
break;
case 'location':
$query->OrderLocation($order);
break;
case 'rtd_location':
$query->OrderRtdLocation($order);
break;
case 'status_label':
$query->OrderStatus($order);
break;
case 'supplier':
$query->OrderSupplier($order);
break;
case 'assigned_to':
$query->OrderAssigned($order);
break;
default:
$query->orderBy($column_sort, $order);
break;
}
$assets = $query->get();
if ($assets->isEmpty()) {
Log::debug('No assets were found for the provided IDs', ['ids' => $asset_ids]);
@@ -110,6 +161,7 @@ class BulkAssetsController extends Controller
$models = $assets->unique('model_id');
$modelNames = [];
foreach($models as $model) {
$modelNames[] = $model->model->name;
}
@@ -145,7 +197,6 @@ class BulkAssetsController extends Controller
case 'edit':
$this->authorize('update', Asset::class);
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
@@ -154,40 +205,7 @@ class BulkAssetsController extends Controller
}
}
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
case 'company':
$assets->OrderCompany($order);
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
break;
case 'supplier':
$assets->OrderSupplier($order);
break;
case 'assigned_to':
$assets->OrderAssigned($order);
break;
default:
$assets->orderBy($column_sort, $order);
break;
}
return redirect()->back()->with('error', 'No action selected');
}
@@ -206,14 +224,26 @@ class BulkAssetsController extends Controller
$error_array = array();
// Get the back url from the session and then destroy the session
$bulk_back_url = route('hardware.index');
if ($request->session()->has('bulk_back_url')) {
$bulk_back_url = $request->session()->pull('bulk_back_url');
}
$bulk_back_url = $request->session()->pull('bulk_back_url', url()->previous());
$custom_field_columns = CustomField::all()->pluck('db_column')->toArray();
// find custom field input attributes that start with 'null_'
$null_custom_fields_inputs = array_filter($request->all(), function ($key) {
// filter out all keys that start with 'null_'
return (strpos($key, 'null_') === 0);
}, ARRAY_FILTER_USE_KEY);;
// remove 'null' from the keys
$custom_fields_to_null = [];
foreach ($null_custom_fields_inputs as $key => $value) {
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
}
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
@@ -251,7 +281,9 @@ class BulkAssetsController extends Controller
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
|| ($request->filled('null_asset_eol_date'))
|| ($request->filled('null_notes'))
|| ($request->anyFilled($custom_field_columns))
|| ($request->anyFilled(array_keys($null_custom_fields_inputs)))
) {
// Let's loop through those assets and build an update array
@@ -274,10 +306,14 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date')
->conditionallyAddItem('asset_eol_date');
->conditionallyAddItem('asset_eol_date')
->conditionallyAddItem('notes');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
$this->conditionallyAddItem($key);
}
if (!($asset->eol_explicit)) {
if ($request->filled('model_id')) {
@@ -328,6 +364,10 @@ class BulkAssetsController extends Controller
}
}
if ($request->input('null_notes')=='1') {
$this->update_array['notes'] = null;
}
if ($request->filled('purchase_cost')) {
@@ -368,10 +408,12 @@ class BulkAssetsController extends Controller
// This could probably be added to a form request.
// If the asset isn't assigned, we don't care what the status is.
// Otherwise we need to make sure the status type is still a deployable one.
if (
($asset->assigned_to == '')
|| ($updated_status->deployable == '1') && ($asset->assetstatus?->deployable == '1')
) {
$unassigned = $asset->assigned_to == '';
$deployable = $updated_status->deployable == '1' && $asset->assetstatus?->deployable == '1';
$pending = $updated_status->pending === 1;
if ($unassigned || $deployable || $pending) {
$this->update_array['status_id'] = $updated_status->id;
}
@@ -423,6 +465,7 @@ class BulkAssetsController extends Controller
}
/**
*
* Start all the custom fields shenanigans
*/
@@ -430,6 +473,15 @@ class BulkAssetsController extends Controller
if ($asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
// null custom fields
if ($custom_fields_to_null) {
foreach ($custom_fields_to_null as $key => $custom_field_to_null) {
if ($field->db_column == $key) {
$this->update_array[$field->db_column] = null;
}
}
}
if ((array_key_exists($field->db_column, $this->update_array)) && ($field->field_encrypted == '1')) {
if (Gate::allows('admin')) {
$decrypted_old = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
@@ -488,7 +540,13 @@ class BulkAssetsController extends Controller
} // end asset foreach
if ($has_errors > 0) {
return redirect($bulk_back_url)->with('bulk_asset_errors', $error_array);
session()->put('bulkedit_ids', $request->input('ids'));
session()->put('bulk_asset_errors',$error_array);
return redirect()
->route('hardware.bulkedit')
->with('bulk_asset_errors', $error_array)
->withInput();
}
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.update.success'));
@@ -562,7 +620,10 @@ class BulkAssetsController extends Controller
public function showCheckout() : View
{
$this->authorize('checkout', Asset::class);
return view('hardware/bulk-checkout');
$do_not_change = ['' => trans('general.do_not_change')];
$status_label_list = $do_not_change + Helper::deployableStatusLabelList();
return view('hardware/bulk-checkout')->with('statusLabel_list', $status_label_list);
}
/**
@@ -594,13 +655,13 @@ class BulkAssetsController extends Controller
}
$checkout_at = date('Y-m-d H:i:s');
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
$checkout_at = e($request->get('checkout_at'));
$checkout_at = $request->get('checkout_at');
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = e($request->get('expected_checkin'));
$expected_checkin = $request->get('expected_checkin');
}
$errors = [];
@@ -608,6 +669,11 @@ class BulkAssetsController extends Controller
foreach ($assets as $asset) {
$this->authorize('checkout', $asset);
// See if there is a status label passed
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
}
$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?
@@ -651,4 +717,54 @@ class BulkAssetsController extends Controller
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
}
}
public function hasUndeployableStatus (array $asset_ids)
{
$undeployable = Asset::whereIn('id', $asset_ids)
->undeployable()
->get();
$undeployableTags = $undeployable->map(function ($asset) {
return [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
})->toArray();
$undeployableIds = array_column($undeployableTags, 'id');
$filtered_ids = array_diff($asset_ids, $undeployableIds);
if($undeployable->isNotEmpty()) {
return ['status' => true, 'tags' => $undeployableTags, 'asset_ids' => $filtered_ids];
}
return false;
}
public function bulkEditForm(): View|RedirectResponse
{
$this->authorize('update', Asset::class);
$asset_ids = session()->pull('bulkedit_ids', []);
if (empty($asset_ids)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.no_assets_selected'));
}
$assets = Asset::with('model')->withTrashed()->whereIn('id', $asset_ids)->get();
if ($assets->isEmpty()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.update.assets_do_not_exist_or_are_invalid'));
}
$models = $assets->unique('model_id');
$modelNames = [];
foreach ($models as $model) {
$modelNames[] = $model->model->name;
}
return view('hardware/bulk')
->with('assets', $asset_ids)
->with('statuslabel_list', Helper::statusLabelList())
->with('models', $models->pluck(['model']))
->with('modelNames', $modelNames);
}
}

View File

@@ -227,7 +227,7 @@ class LoginController extends Controller
$strip_prefixes = [
// IIS/AD
// https://github.com/snipe/snipe-it/pull/5862
// https://github.com/grokability/snipe-it/pull/5862
'\\',
// Google Cloud IAP
@@ -484,6 +484,7 @@ class LoginController extends Controller
}
$request->session()->regenerate(true);
$request->session()->forget('2fa_authed');
if ($request->session()->has('password_hash_'.Auth::getDefaultDriver())){
$request->session()->remove('password_hash_'.Auth::getDefaultDriver());

View File

@@ -68,6 +68,7 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
$category->created_by = auth()->id();
@@ -121,6 +122,7 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
@@ -145,7 +147,7 @@ class CategoriesController extends Controller
{
$this->authorize('delete', Category::class);
// Check if the category exists
if (is_null($category = Category::findOrFail($categoryId))) {
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
}
@@ -155,7 +157,6 @@ class CategoriesController extends Controller
Storage::disk('public')->delete('categories'.'/'.$category->image);
$category->delete();
// Redirect to the locations management page
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
}

View File

@@ -123,11 +123,13 @@ final class CompaniesController extends Controller
*/
public function destroy($companyId) : RedirectResponse
{
if (is_null($company = Company::find($companyId))) {
return redirect()->route('companies.index')
->with('error', trans('admin/companies/message.not_found'));
}
$this->authorize('delete', $company);
if (! $company->isDeletable()) {
return redirect()->route('companies.index')

View File

@@ -100,8 +100,8 @@ class ComponentCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success',
trans('admin/components/message.checkin.success'));
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkin.success'));
}
return redirect()->route('components.index')->with('error', trans('admin/components/message.does_not_exist'));

View File

@@ -120,6 +120,7 @@ class ComponentCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.checkout.success'));
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkout.success'));
}
}

View File

@@ -91,7 +91,8 @@ class ComponentsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
@@ -111,6 +112,7 @@ class ComponentsController extends Controller
{
$this->authorize('update', $component);
session()->put('back_url', url()->previous());
return view('components/edit')
->with('item', $component)
->with('category_type', 'component');
@@ -164,7 +166,8 @@ class ComponentsController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());

View File

@@ -111,6 +111,7 @@ class ConsumableCheckoutController extends Controller
// Redirect to the new consumable page
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.checkout.success'));
}
}

View File

@@ -90,7 +90,8 @@ class ConsumablesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -107,6 +108,7 @@ class ConsumablesController extends Controller
public function edit(Consumable $consumable) : View | RedirectResponse
{
$this->authorize($consumable);
session()->put('back_url', url()->previous());
return view('consumables/edit')
->with('item', $consumable)
->with('category_type', 'consumable');
@@ -160,7 +162,8 @@ class ConsumablesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
->with('success', trans('admin/consumables/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());

View File

@@ -83,30 +83,30 @@ class CustomFieldsController extends Controller
{
$this->authorize('create', CustomField::class);
$show_in_email = $request->get("show_in_email", 0);
$display_in_user_view = $request->get("display_in_user_view", 0);
$show_in_email = $request->input("show_in_email", 0);
$display_in_user_view = $request->input("display_in_user_view", 0);
// Override the display settings if the field is encrypted
if ($request->get("field_encrypted") == '1') {
if ($request->input("field_encrypted") == '1') {
$show_in_email = '0';
$display_in_user_view = '0';
}
$field = new CustomField([
"name" => trim($request->get("name")),
"element" => $request->get("element"),
"help_text" => $request->get("help_text"),
"field_values" => $request->get("field_values"),
"field_encrypted" => $request->get("field_encrypted", 0),
"name" => trim($request->input("name")),
"element" => $request->input("element"),
"help_text" => $request->input("help_text"),
"field_values" => $request->input("field_values"),
"field_encrypted" => $request->input("field_encrypted", 0),
"show_in_email" => $show_in_email,
"is_unique" => $request->get("is_unique", 0),
"is_unique" => $request->input("is_unique", 0),
"display_in_user_view" => $display_in_user_view,
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->get("show_in_listview", 0),
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
"display_checkin" => $request->get("display_checkin", 0),
"display_checkout" => $request->get("display_checkout", 0),
"display_audit" => $request->get("display_audit", 0),
"auto_add_to_fieldsets" => $request->input("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->input("show_in_listview", 0),
"show_in_requestable_list" => $request->input("show_in_requestable_list", 0),
"display_checkin" => $request->input("display_checkin", 0),
"display_checkout" => $request->input("display_checkout", 0),
"display_audit" => $request->input("display_audit", 0),
"created_by" => auth()->id()
]);
@@ -238,8 +238,8 @@ class CustomFieldsController extends Controller
$display_in_user_view = '0';
}
$field->name = trim(e($request->get("name")));
$field->element = e($request->get("element"));
$field->name = trim($request->get("name"));
$field->element = $request->get("element");
$field->field_values = $request->get("field_values");
$field->created_by = auth()->id();
$field->help_text = $request->get("help_text");
@@ -254,9 +254,9 @@ class CustomFieldsController extends Controller
$field->display_audit = $request->get("display_audit", 0);
if ($request->get('format') == 'CUSTOM REGEX') {
$field->format = e($request->get('custom_format'));
$field->format = $request->get('custom_format');
} else {
$field->format = e($request->get('format'));
$field->format = $request->get('format');
}
if ($field->element == 'checkbox' || $field->element == 'radio'){

View File

@@ -86,7 +86,8 @@ class LicenseCheckinController extends Controller
}
if($licenseSeat->assigned_to != null){
$return_to = User::find($licenseSeat->assigned_to);
$return_to = User::withTrashed()->find($licenseSeat->assigned_to);
session()->put('checkedInFrom', $return_to->id);
} else {
$return_to = Asset::find($licenseSeat->asset_id);
}
@@ -97,14 +98,17 @@ class LicenseCheckinController extends Controller
$licenseSeat->notes = $request->input('notes');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($request->get('redirect_option') === 'target'){
session()->put(['checkout_to_type' => 'user']);
}
// Was the asset updated?
if ($licenseSeat->save()) {
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.checkin.success'));
}
// Redirect to the license page with error

View File

@@ -89,7 +89,8 @@ class LicenseCheckoutController extends Controller
if ($checkoutTarget) {
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.checkout.success'));
}

View File

@@ -105,7 +105,8 @@ class LicensesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($license->getErrors());
@@ -125,7 +126,7 @@ class LicensesController extends Controller
{
$this->authorize('update', $license);
session()->put('back_url', url()->previous());
$maintained_list = [
'' => 'Maintained',
'1' => 'Yes',
@@ -181,7 +182,8 @@ class LicensesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.update.success'));
return Helper::getRedirectOption($request, $license->id, 'Licenses')
->with('success', trans('admin/licenses/message.update.success'));
}
// If we can't adjust the number of seats, the error is flashed to the session by the event handler in License.php
return redirect()->back()->withInput()->withErrors($license->getErrors());

View File

@@ -66,6 +66,7 @@ class LocationsController extends Controller
public function store(ImageUploadRequest $request) : RedirectResponse
{
$this->authorize('create', Location::class);
$location = new Location();
$location->name = $request->input('name');
$location->parent_id = $request->input('parent_id', null);
@@ -150,7 +151,7 @@ class LocationsController extends Controller
if (Setting::getSettings()->scope_locations_fmcs) {
$location->company_id = Company::getIdForCurrentUser($request->input('company_id'));
// check if there are related objects with different company
if (Helper::test_locations_fmcs(false, $locationId, $location->company_id)) {
if (Helper::test_locations_fmcs(false, $location->id, $location->company_id)) {
return redirect()->back()->withInput()->withInput()->with('error', 'error scoped locations');
}
} else {
@@ -176,6 +177,7 @@ class LocationsController extends Controller
public function destroy($locationId) : RedirectResponse
{
$this->authorize('delete', Location::class);
if (is_null($location = Location::find($locationId))) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
}
@@ -212,6 +214,8 @@ class LocationsController extends Controller
*/
public function show(Location $location) : View | RedirectResponse
{
$this->authorize('view', Location::class);
$location = Location::withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('rtd_assets as rtd_assets_count')
@@ -229,6 +233,8 @@ class LocationsController extends Controller
public function print_assigned($id) : View | RedirectResponse
{
$this->authorize('view', Location::class);
if ($location = Location::where('id', $id)->first()) {
$parent = Location::where('id', $location->parent_id)->first();
$manager = User::where('id', $location->manager_id)->first();
@@ -313,6 +319,7 @@ class LocationsController extends Controller
}
public function print_all_assigned($id) : View | RedirectResponse
{
$this->authorize('view', Location::class);
if ($location = Location::where('id', $id)->first()) {
$parent = Location::where('id', $location->parent_id)->first();
$manager = User::where('id', $location->manager_id)->first();
@@ -339,6 +346,8 @@ class LocationsController extends Controller
*/
public function postBulkDelete(Request $request) : View | RedirectResponse
{
$this->authorize('update', Location::class);
$locations_raw_array = $request->input('ids');
// Make sure some IDs have been selected
@@ -372,6 +381,8 @@ class LocationsController extends Controller
*/
public function postBulkDeleteStore(Request $request) : RedirectResponse
{
$this->authorize('delete', Location::class);
$locations_raw_array = $request->input('ids');
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {

View File

@@ -0,0 +1,111 @@
<?php
namespace App\Http\Controllers;
use App\Helpers\StorageHelper;
use App\Http\Requests\UploadFileRequest;
use App\Models\Actionlog;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use \Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class LocationsFilesController extends Controller
{
/**
* Upload a file to the server.
*
* @param UploadFileRequest $request
* @param int $modelId
* @return \Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException
*@since [v1.0]
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function store(UploadFileRequest $request, Location $location) : RedirectResponse
{
$this->authorize('update', $location);
if ($request->hasFile('file')) {
if (! Storage::exists('private_uploads/locations')) {
Storage::makeDirectory('private_uploads/locations', 775);
}
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile('private_uploads/locations/','location-'.$location->id, $file);
$location->logUpload($file_name, $request->get('notes'));
}
return redirect()->back()->withFragment('files')->with('success', trans('general.file_upload_success'));
}
return redirect()->back()->withFragment('files')->with('error', trans('admin/hardware/message.upload.nofiles'));
}
/**
* Check for permissions and display the file.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function show(Location $location, $fileId = null) : StreamedResponse | Response | RedirectResponse | BinaryFileResponse
{
$this->authorize('view', $location);
if (! $log = Actionlog::find($fileId)) {
return redirect()->back()->withFragment('files')->with('error', 'No matching file record');
}
$file = 'private_uploads/locations/'.$log->filename;
if (! Storage::exists($file)) {
return redirect()->back()->withFragment('files')->with('error', 'No matching file on server');
}
if (request('inline') == 'true') {
$headers = [
'Content-Disposition' => 'inline',
];
return Storage::download($file, $log->filename, $headers);
}
return StorageHelper::downloader($file);
}
/**
* Delete the associated file
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $modelId
* @param int $fileId
* @since [v1.0]
*/
public function destroy(Location $location, $fileId = null) : RedirectResponse
{
$rel_path = 'private_uploads/locations';
$this->authorize('update', $location);
$log = Actionlog::find($fileId);
if ($log) {
// This should be moved to purge
// if (Storage::exists($rel_path.'/'.$log->filename)) {
// Storage::delete($rel_path.'/'.$log->filename);
// }
$log->delete();
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
return redirect()->back()->withFragment('files')->with('success', trans('admin/hardware/message.deletefile.success'));
}
}

View File

@@ -6,6 +6,7 @@ use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Manufacturer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
@@ -31,7 +32,30 @@ class ManufacturersController extends Controller
public function index() : View
{
$this->authorize('index', Manufacturer::class);
return view('manufacturers/index');
$manufacturer_count = Manufacturer::withTrashed()->count();
return view('manufacturers/index')->with('manufacturer_count', $manufacturer_count);
}
/**
* Returns a view that invokes the ajax tables which actually contains
* the content for the manufacturers listing, which is generated in getDatatable.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @see Api\ManufacturersController::index() method that generates the JSON response
* @since [v1.0]
*/
public function seed() : RedirectResponse
{
$this->authorize('index', Manufacturer::class);
$manufacturers_count = Manufacturer::withTrashed()->count();
if ($manufacturers_count == 0) {
Artisan::call('db:seed', ['--class' => 'ManufacturerSeeder']);
return redirect()->route('manufacturers.index')->with('success', trans('general.seeding.manufacturers.success'));
}
return redirect()->route('manufacturers.index')->with('error', trans_choice('general.seeding.manufacturers.error', ['count' => $manufacturers_count]));
}
/**

View File

@@ -3,15 +3,21 @@
namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\ProfileTransformer;
use App\Models\Actionlog;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This controller handles all actions related to User Profiles for
* the Snipe-IT Asset Management application.
@@ -53,7 +59,7 @@ class ProfileController extends Controller
$user->enable_confetti = $request->input('enable_confetti', false);
if (! config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en-US');
$user->locale = $request->input('locale');
}
if ((Gate::allows('self.two_factor')) && ((Setting::getSettings()->two_factor_enabled == '1') && (! config('app.lock_passwords')))) {
@@ -136,7 +142,7 @@ class ProfileController extends Controller
}
// This checks to make sure that the user's password isn't the same as their username,
// email address, first name or last name (see https://github.com/snipe/snipe-it/issues/8661)
// email address, first name or last name (see https://github.com/grokability/snipe-it/issues/8661)
// While this is handled via SaveUserRequest form request in other places, we have to do this manually
// here because we don't have the username, etc form fields available in the profile password change
// form.
@@ -220,7 +226,7 @@ class ProfileController extends Controller
if (!$user = User::find(auth()->id())) {
return redirect()->back()
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
}
if (empty($user->email)) {
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
@@ -234,4 +240,28 @@ class ProfileController extends Controller
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
}
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$logentry = Actionlog::where('filename', $filename)->first();
// Make sure the user has permission to view this file
if (auth()->id() != $logentry->target_id) {
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
}
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View File

@@ -184,7 +184,7 @@ class ReportsController extends Controller
$currency = e(Setting::getSettings()->default_currency);
}
$row[] = $asset->purchase_date;
$row[] = Helper::getFormattedDateObject($asset->purchase_date, 'date', false);
$row[] = $currency.Helper::formatCurrencyOutput($asset->purchase_cost);
$row[] = $currency.Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$row[] = $currency.Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
@@ -485,7 +485,7 @@ class ReportsController extends Controller
$header[] = trans('admin/hardware/table.purchase_date');
}
if (($request->filled('purchase_cost')) || ($request->filled('depreciation'))) {
if ($request->filled('purchase_cost')) {
$header[] = trans('admin/hardware/table.purchase_cost');
}
@@ -737,6 +737,11 @@ class ReportsController extends Controller
if (($request->filled('next_audit_start')) && ($request->filled('next_audit_end'))) {
$assets->whereBetween('assets.next_audit_date', [$request->input('next_audit_start'), $request->input('next_audit_end')]);
}
if (($request->filled('last_updated_start')) && ($request->filled('last_updated_end'))) {
$assets->whereBetween('assets.updated_at', [$request->input('last_updated_start'), $request->input('last_updated_end')]);
}
if ($request->filled('exclude_archived')) {
$assets->notArchived();
}

View File

@@ -14,6 +14,7 @@ use App\Http\Requests\StoreLabelSettings;
use App\Http\Requests\StoreSecuritySettings;
use App\Models\CustomField;
use App\Models\Group;
use App\Models\Labels\Label as LabelModel;
use App\Models\Setting;
use App\Models\Asset;
use App\Models\User;
@@ -290,7 +291,6 @@ class SettingsController extends Controller
public function getSettings() : View
{
$setting = Setting::getSettings();
return view('settings/general', compact('setting'));
}
@@ -352,6 +352,7 @@ class SettingsController extends Controller
$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);
$setting->manager_view_enabled = $request->input('manager_view_enabled', 0);
if ($request->input('per_page') != '') {
@@ -650,6 +651,7 @@ class SettingsController extends Controller
$setting->alert_email = $alert_email;
$setting->admin_cc_email = $admin_cc_email;
$setting->admin_cc_always = $request->validated('admin_cc_always');
$setting->alerts_enabled = $request->input('alerts_enabled', '0');
$setting->alert_interval = $request->input('alert_interval');
$setting->alert_threshold = $request->input('alert_threshold');
@@ -772,6 +774,7 @@ class SettingsController extends Controller
$setting->label2_2d_type = $request->input('label2_2d_type');
$setting->label2_2d_target = $request->input('label2_2d_target');
$setting->label2_fields = $request->input('label2_fields');
$setting->label2_empty_row_count = $request->input('label2_empty_row_count');
$setting->labels_per_page = $request->input('labels_per_page');
$setting->labels_width = $request->input('labels_width');
$setting->labels_height = $request->input('labels_height');
@@ -921,7 +924,7 @@ class SettingsController extends Controller
* @since v5.0.0
*/
public function postSamlSettings(SettingsSamlRequest $request) : RedirectResponse
{
{
if (is_null($setting = Setting::getSettings())) {
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}

View File

@@ -298,7 +298,6 @@ class BulkUsersController extends Controller
$this->logItemCheckinAndDelete($assets, Asset::class);
$this->logAccessoriesCheckin($accessoryUserRows);
$this->logItemCheckinAndDelete($licenses, License::class);
$this->logConsumablesCheckin($consumableUserRows);
Asset::whereIn('id', $assets->pluck('id'))->update([
'status_id' => e(request('status_id')),
@@ -366,20 +365,6 @@ class BulkUsersController extends Controller
}
}
private function logConsumablesCheckin(Collection $consumableUserRows): void
{
foreach ($consumableUserRows as $consumableUserRow) {
$logAction = new Actionlog();
$logAction->item_id = $consumableUserRow->consumable_id;
$logAction->item_type = Consumable::class;
$logAction->target_id = $consumableUserRow->assigned_to;
$logAction->target_type = User::class;
$logAction->created_by = auth()->id();
$logAction->note = 'Bulk checkin items';
$logAction->logaction('checkin from');
}
}
/**
* Save bulk-edited users
*

View File

@@ -13,15 +13,8 @@ use App\Models\Company;
use App\Models\Group;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Storage;
use Redirect;
use Str;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Notifications\CurrentInventory;
@@ -130,7 +123,7 @@ class UsersController extends Controller
}
$user->permissions = json_encode($permissions_array);
// we have to invoke the
// we have to invoke the form request here to handle image uploads
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
session()->put(['redirect_option' => $request->get('redirect_option')]);
@@ -142,19 +135,8 @@ class UsersController extends Controller
$user->groups()->sync([]);
}
if (($request->input('email_user') == 1) && ($request->filled('email'))) {
// Send the credentials through email
$data = [];
$data['email'] = e($request->input('email'));
$data['username'] = e($request->input('username'));
$data['first_name'] = e($request->input('first_name'));
$data['last_name'] = e($request->input('last_name'));
$data['password'] = e($request->input('password'));
$user->notify(new WelcomeNotification($data));
}
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.create'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -178,7 +160,7 @@ class UsersController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v1.0]
* @param $permissions
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
* @internal param int $id
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
@@ -186,10 +168,15 @@ class UsersController extends Controller
{
$this->authorize('update', User::class);
session()->put('back_url', url()->previous());
$user = User::with(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed()->find($user->id);
if ($user) {
if ($user->trashed()) {
return redirect()->route('users.show', $user->id);
}
$permissions = config('permissions');
$groups = Group::pluck('name', 'id');
@@ -242,20 +229,14 @@ class UsersController extends Controller
}
}
// Only save groups if the user is a superuser
if (auth()->user()->isSuperUser()) {
$user->groups()->sync($request->input('groups'));
}
// Update the user fields
$user->username = trim($request->input('username'));
$user->email = trim($request->input('email'));
$user->first_name = $request->input('first_name');
$user->last_name = $request->input('last_name');
$user->two_factor_optin = $request->input('two_factor_optin') ?: 0;
$user->locale = $request->input('locale');
$user->employee_num = $request->input('employee_num');
$user->activated = $request->input('activated', 0);
$user->jobtitle = $request->input('jobtitle', null);
$user->phone = $request->input('phone');
$user->location_id = $request->input('location_id', null);
@@ -267,8 +248,6 @@ class UsersController extends Controller
$user->city = $request->input('city', null);
$user->state = $request->input('state', null);
$user->country = $request->input('country', null);
// if a user is editing themselves we should always keep activated true
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
$user->zip = $request->input('zip', null);
$user->remote = $request->input('remote', 0);
$user->vip = $request->input('vip', 0);
@@ -277,30 +256,49 @@ class UsersController extends Controller
$user->end_date = $request->input('end_date', null);
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
// Set this here so that we can overwrite it later if the user is an admin or superadmin
$user->activated = $request->input('activated', auth()->user()->is($user) ? 1 : $user->activated);
// 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)]);
// Do we want to update the user password?
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
// check for permissions related fields and only set them if the user has permission to edit them
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->username = trim($request->input('username'));
$user->email = trim($request->input('email'));
$user->activated = $request->input('activated', $request->user()->is($user) ? 1 : 0);
// Do we want to update the user password?
if ($request->filled('password')) {
$user->password = bcrypt($request->input('password'));
}
$permissions_array = $request->input('permission');
// Strip out the superuser permission if the user isn't a superadmin
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
$permissions_array['superuser'] = $orig_superuser;
}
$user->permissions = json_encode($permissions_array);
// Only save groups if the user is a superuser
if (auth()->user()->isSuperUser()) {
$user->groups()->sync($request->input('groups'));
}
}
// 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' => $user->location_id]);
$permissions_array = $request->input('permission');
// Strip out the superuser permission if the user isn't a superadmin
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
$permissions_array['superuser'] = $orig_superuser;
}
$user->permissions = json_encode($permissions_array);
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
@@ -308,7 +306,7 @@ class UsersController extends Controller
if ($user->save()) {
// Redirect to the user page
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.update'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -506,6 +504,8 @@ class UsersController extends Controller
trans('admin/companies/table.title'),
trans('admin/users/table.title'),
trans('general.employee_number'),
trans('admin/users/table.first_name'),
trans('admin/users/table.last_name'),
trans('admin/users/table.name'),
trans('admin/users/table.username'),
trans('admin/users/table.email'),
@@ -551,6 +551,8 @@ class UsersController extends Controller
($user->company) ? $user->company->name : '',
$user->jobtitle,
$user->employee_num,
$user->first_name,
$user->last_name,
$user->present()->fullName(),
$user->username,
$user->email,
@@ -594,18 +596,18 @@ class UsersController extends Controller
$user = User::where('id', $id)
->with([
'assets.assetlog',
'assets.assignedAssets.assetlog',
'assets.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
'assets.assignedAssets.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
'assets.assignedAssets.defaultLoc',
'assets.assignedAssets.location',
'assets.assignedAssets.model.category',
'assets.defaultLoc',
'assets.location',
'assets.model.category',
'accessories.assetlog',
'accessories.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
'accessories.category',
'accessories.manufacturer',
'consumables.assetlog',
'consumables.log' => fn($query) => $query->withTrashed()->where('target_type', User::class)->where('target_id', $id)->where('action_type', 'accepted'),
'consumables.category',
'consumables.manufacturer',
'licenses.category',

View File

@@ -27,50 +27,126 @@ use Exception;
class ViewAssetsController extends Controller
{
/**
* Redirect to the profile page.
* Extract custom fields that should be displayed in user view.
*
* @param User $user
* @return array
*/
private function extractCustomFields(User $user): array
{
$fieldArray = [];
foreach ($user->assets as $asset) {
if ($asset->model && $asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
if ($field->display_in_user_view == '1') {
$fieldArray[$field->db_column] = $field->name;
}
}
}
}
return array_unique($fieldArray);
}
/**
* Get list of users viewable by the current user.
*
* @param User $authUser
* @return \Illuminate\Support\Collection
*/
private function getViewableUsers(User $authUser): \Illuminate\Support\Collection
{
// SuperAdmin sees all users
if ($authUser->isSuperUser()) {
return User::select('id', 'first_name', 'last_name', 'username')
->where('activated', 1)
->orderBy('last_name')
->orderBy('first_name')
->get();
}
// Regular manager sees only their subordinates + self
$managedUsers = $authUser->getAllSubordinates();
// If user has subordinates, show them with self at beginning
if ($managedUsers->count() > 0) {
return collect([$authUser])->merge($managedUsers)
->sortBy('last_name')
->sortBy('first_name');
}
// User has no subordinates, only sees themselves
return collect([$authUser]);
}
/**
* Get the selected user ID from request or default to current user.
*
* @param Request $request
* @param \Illuminate\Support\Collection $subordinates
* @param int $defaultUserId
* @return int
*/
private function getSelectedUserId(Request $request, \Illuminate\Support\Collection $subordinates, int $defaultUserId): int
{
// If no subordinates or no user_id in request, return default
if ($subordinates->count() <= 1 || !$request->filled('user_id')) {
return $defaultUserId;
}
$requestedUserId = (int) $request->input('user_id');
// Validate if the requested user is allowed
if ($subordinates->contains('id', $requestedUserId)) {
return $requestedUserId;
}
// If invalid ID or not authorized, return default
return $defaultUserId;
}
/**
* Show user's assigned assets with optional manager view functionality.
*
*/
public function getIndex() : View | RedirectResponse
public function getIndex(Request $request) : View | RedirectResponse
{
$user = User::with(
$authUser = auth()->user();
$settings = Setting::getSettings();
$subordinates = collect();
$selectedUserId = $authUser->id;
// Process manager view if enabled
if ($settings->manager_view_enabled) {
$subordinates = $this->getViewableUsers($authUser);
$selectedUserId = $this->getSelectedUserId($request, $subordinates, $authUser->id);
}
// Load the data for the user to be viewed (either auth user or selected subordinate)
$userToView = User::with([
'assets',
'assets.model',
'assets.model.fieldset.fields',
'consumables',
'accessories',
'licenses',
)->find(auth()->id());
$field_array = array();
// Loop through all the custom fields that are applied to any model the user has assigned
foreach ($user->assets as $asset) {
// Make sure the model has a custom fieldset before trying to loop through the associated fields
if ($asset->model->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
// check and make sure they're allowed to see the value of the custom field
if ($field->display_in_user_view == '1') {
$field_array[$field->db_column] = $field->name;
}
}
}
'licenses'
])->find($selectedUserId);
// If the user to view couldn't be found (shouldn't happen with proper logic), redirect with error
if (!$userToView) {
return redirect()->route('view-assets')->with('error', trans('admin/users/message.user_not_found'));
}
// Since some models may re-use the same fieldsets/fields, let's make the array unique so we don't repeat columns
array_unique($field_array);
// Process custom fields for the user being viewed
$fieldArray = $this->extractCustomFields($userToView);
if (isset($user->id)) {
return view('account/view-assets', compact('user', 'field_array' ))
->with('settings', Setting::getSettings());
}
// Redirect to the user management page
return redirect()->route('users.index')
->with('error', trans('admin/users/message.user_not_found', $user->id));
// Pass the necessary data to the view
return view('account/view-assets', [
'user' => $userToView, // Use 'user' for compatibility with the existing view
'field_array' => $fieldArray,
'settings' => $settings,
'subordinates' => $subordinates,
'selectedUserId' => $selectedUserId
]);
}
/**

View File

@@ -73,6 +73,7 @@ class Kernel extends HttpKernel
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'api-throttle' => \App\Http\Middleware\SetAPIResponseHeaders::class,
'health' => null,
];
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Symfony\Component\HttpFoundation\Response;
class SetAPIResponseHeaders extends ThrottleRequests
{
/**
* Add the rate limit headers to the response.
*
* This extends the original ThrottleRequests middleware to add the 'X-RateLimit-Reset' and 'Retry-After' headers, even
* if the rate limit is not exceeded.
* @param $maxAttempts
* @param $remainingAttempts
* @param $retryAfter
* @param Response|null $response
* @return array|int[]
*/
protected function getHeaders($maxAttempts, $remainingAttempts, $retryAfter = null, ?Response $response = null)
{
if ($response &&
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
$headers = [];
$headers['Retry-After'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
return $headers;
}
$headers = [
'X-RateLimit-Limit' => $maxAttempts,
'X-RateLimit-Remaining' => $remainingAttempts,
];
if (! is_null($retryAfter)) {
$headers['Retry-After'] = $retryAfter;
$headers['X-RateLimit-Reset'] = $retryAfter; // this is the only line we changed
$headers['X-RateLimit-Reset-Timestamp'] = $this->availableAt($retryAfter); // this is the only line we changed
}
return $headers;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
protected function handleRequest($request, Closure $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
}
$this->limiter->hit($limit->key, $limit->decaySeconds);
}
$response = $next($request);
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts),
$this->getTimeUntilNextRetry($limit->key) // this is the only line we changed
);
}
return $response;
}
}

View File

@@ -41,6 +41,7 @@ class SettingsSamlRequest extends FormRequest
public function withValidator($validator)
{
$validator->after(function ($validator) {
$setting = Setting::getSettings();
if ($this->input('saml_enabled') == '1') {
$idpMetadata = $this->input('saml_idp_metadata');
if (! empty($idpMetadata)) {
@@ -56,7 +57,7 @@ class SettingsSamlRequest extends FormRequest
}
}
$was_custom_x509cert = strpos(Setting::getSettings()->saml_custom_settings, 'sp_x509cert') !== false;
$was_custom_x509cert = strpos($setting->saml_custom_settings, 'sp_x509cert') !== false;
$custom_x509cert = '';
$custom_privateKey = '';
@@ -126,10 +127,14 @@ class SettingsSamlRequest extends FormRequest
}
if (! (empty($x509cert) && empty($privateKey))) {
$this->merge([
'saml_sp_x509cert' => $x509cert,
'saml_sp_privatekey' => $privateKey,
]);
// $this->merge([
// 'saml_sp_x509cert' => $x509cert,
// 'saml_sp_privatekey' => $privateKey,
// ]);
$setting->saml_sp_x509cert = $x509cert;
$setting->saml_sp_privatekey = $privateKey;
$setting->save();
}
} else {
$validator->errors()->add('saml_integration', 'openssl.cnf is missing/invalid');
@@ -145,15 +150,21 @@ class SettingsSamlRequest extends FormRequest
}
if (! empty($x509certNew)) {
$this->merge([
'saml_sp_x509certNew' => $x509certNew,
]);
// $this->merge([
// 'saml_sp_x509certNew' => $x509certNew,
// ]);
$setting->saml_sp_x509certNew = $x509certNew;
$setting->save();
}
} else {
$this->merge([
'saml_sp_x509certNew' => '',
]);
// $this->merge([
// 'saml_sp_x509certNew' => '',
// ]);
$setting->saml_sp_x509certNew = '';
$setting->save();
}
});
}
}

View File

@@ -19,6 +19,7 @@ class StoreAssetModelRequest extends ImageUploadRequest
public function prepareForValidation(): void
{
parent::prepareForValidation();
if ($this->category_id) {
if ($category = Category::find($this->category_id)) {

View File

@@ -39,7 +39,6 @@ class StoreAssetRequest extends ImageUploadRequest
$this->merge([
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
'company_id' => $idForCurrentUser,
'assigned_to' => $assigned_to ?? null,
]);
}

View File

@@ -37,8 +37,8 @@ class StoreLabelSettings extends FormRequest
return [
'labels_per_page' => 'numeric',
'labels_width' => 'numeric',
'labels_height' => 'numeric',
'labels_width' => 'numeric|min:0.1',
'labels_height' => 'numeric|min:0.1',
'labels_pmargin_left' => 'numeric|nullable',
'labels_pmargin_right' => 'numeric|nullable',
'labels_pmargin_top' => 'numeric|nullable',

View File

@@ -27,8 +27,6 @@ class StoreLdapSettings extends FormRequest
'ldap_auth_filter_query' => 'not_in:uid=samaccountname|required_if:ldap_enabled,1',
'ldap_filter' => 'nullable|regex:"^[^(]"|required_if:ldap_enabled,1',
'ldap_server' => 'nullable|required_if:ldap_enabled,1|starts_with:ldap://,ldaps://',
'ldap_uname' => 'nullable|required_if:ldap_enabled,1',
'ldap_pword' => 'nullable|required_if:ldap_enabled,1',
'ldap_basedn' => 'nullable|required_if:ldap_enabled,1',
'ldap_fname_field' => 'nullable|required_if:ldap_enabled,1',
'custom_forgot_pass_url' => 'nullable|url',

View File

@@ -5,6 +5,7 @@ namespace App\Http\Requests;
use App\Models\Accessory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class StoreNotificationSettings extends FormRequest
{
@@ -26,6 +27,9 @@ class StoreNotificationSettings extends FormRequest
return [
'alert_email' => 'email_array|nullable',
'admin_cc_email' => 'email_array|nullable',
'admin_cc_always' => [
Rule::in('0', '1'),
],
'alert_threshold' => 'numeric|nullable',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable',

View File

@@ -6,6 +6,7 @@ use App\Http\Traits\ConvertsBase64ToFiles;
use enshrined\svgSanitize\Sanitizer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use \App\Helpers\Helper;
class UploadFileRequest extends Request
{
@@ -27,44 +28,76 @@ class UploadFileRequest extends Request
*/
public function rules()
{
$max_file_size = \App\Helpers\Helper::file_upload_max_size();
$max_file_size = Helper::file_upload_max_size();
return [
'file.*' => 'required|mimes:png,gif,jpg,svg,jpeg,doc,docx,pdf,txt,zip,rar,xls,xlsx,lic,xml,rtf,json,webp,avif|max:'.$max_file_size,
'file.*' => 'required|mimes:'.config('filesystems.allowed_upload_extensions_for_validator').'|max:'.$max_file_size,
];
}
/**
* Sanitizes (if needed) and Saves a file to the appropriate location
* Returns the 'short' (storage-relative) filename
*
* TODO - this has a lot of similarities to UploadImageRequest's handleImage; is there
* a way to merge them or extend one into the other?
*/
public function handleFile(string $dirname, string $name_prefix, $file): string
{
$extension = $file->getClientOriginalExtension();
$file_name = $name_prefix.'-'.str_random(8).'-'.str_slug(basename($file->getClientOriginalName(), '.'.$extension)).'.'.$file->guessExtension();
// Check for SVG and sanitize it
if ($file->getMimeType() === 'image/svg+xml') {
Log::debug('This is an SVG');
Log::debug($file_name);
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
$cleanSVG = $sanitizer->sanitize($dirtySVG);
try {
Storage::put($dirname.$file_name, $cleanSVG);
} catch (\Exception $e) {
Log::debug('Upload no workie :( ');
Log::debug($e);
}
$uploaded_file = $this->handleSVG($file);
} else {
$put_results = Storage::put($dirname.$file_name, file_get_contents($file));
$uploaded_file = file_get_contents($file);
}
try {
Storage::put($dirname.$file_name, $uploaded_file);
} catch (\Exception $e) {
Log::debug($e);
}
return $file_name;
}
}
public function handleSVG($file)
{
$sanitizer = new Sanitizer();
$dirtySVG = file_get_contents($file->getRealPath());
return $sanitizer->sanitize($dirtySVG);
}
/**
* Get the validation error messages that apply to the request, but
* replace the attribute name with the name of the file that was attempted and failed
* to make it clearer to the user which file is the bad one.
*
* @return array
*/
public function attributes(): array
{
$attributes = [];
if (($this->file) && (is_array($this->file))) {
for ($i = 0; $i < count($this->file); $i++) {
try {
if ($this->file[$i]) {
$attributes['file.'.$i] = $this->file[$i]->getClientOriginalName();
}
} catch (\Exception $e) {
$attributes['file.'.$i] = 'Invalid file';
}
}
}
return $attributes;
}
}

View File

@@ -55,7 +55,7 @@ class AccessoriesTransformer
'checkout' => Gate::allows('checkout', Accessory::class),
'checkin' => false,
'update' => Gate::allows('update', Accessory::class),
'delete' => Gate::allows('delete', Accessory::class),
'delete' => $accessory->checkouts_count === 0 && Gate::allows('delete', Accessory::class),
'clone' => Gate::allows('create', Accessory::class),
];
@@ -94,6 +94,10 @@ class AccessoriesTransformer
public function transformAssignedTo($accessoryCheckout)
{
if (is_null($accessoryCheckout->assigned)) {
return null;
}
if ($accessoryCheckout->checkedOutToUser()) {
return (new UsersTransformer)->transformUserCompact($accessoryCheckout->assigned);
} elseif ($accessoryCheckout->checkedOutToLocation()) {

View File

@@ -113,8 +113,8 @@ class ActionlogsTransformer
// Display the changes if the user is an admin or superadmin
if (Gate::allows('admin')) {
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old): '';
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new): '';
$clean_meta[$fieldname]['old'] = ($enc_old) ? unserialize($enc_old, ['allowed_classes' => false]) : '';
$clean_meta[$fieldname]['new'] = ($enc_new) ? unserialize($enc_new, ['allowed_classes' => false]) : '';
}
}
@@ -198,8 +198,8 @@ class ActionlogsTransformer
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'remote_ip' => ($actionlog->remote_ip) ?? null,
'user_agent' => ($actionlog->user_agent) ?? null,
'remote_ip' => e($actionlog->remote_ip) ?? null,
'user_agent' => e($actionlog->user_agent) ?? null,
'action_source' => ($actionlog->action_source) ?? null,
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
];

View File

@@ -59,7 +59,10 @@ class AssetMaintenancesTransformer
'name'=> e($assetmaintenance->asset->defaultLoc->name),
] : null,
'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedownInline($assetmaintenance->notes) : null,
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
'supplier' => ($assetmaintenance->supplier) ? [
'id' => $assetmaintenance->supplier->id,
'name'=> e($assetmaintenance->supplier->name)
] : null,
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),

View File

@@ -105,6 +105,7 @@ class AssetModelsTransformer
$array = [
'id' => (int) $file->id,
'filename' => e($file->filename),
'note' => $file->note,
'url' => route('show/modelfile', [$assetmodel->id, $file->id]),
'created_by' => ($file->adminuser) ? [
'id' => (int) $file->adminuser->id,

View File

@@ -101,7 +101,7 @@ class AssetsTransformer
'checkout_counter' => (int) $asset->checkout_counter,
'requests_counter' => (int) $asset->requests_counter,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()),
'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
];
@@ -155,6 +155,7 @@ class AssetsTransformer
'clone' => Gate::allows('create', Asset::class) ? true : false,
'restore' => ($asset->deleted_at!='' && Gate::allows('create', Asset::class)) ? true : false,
'update' => ($asset->deleted_at=='' && Gate::allows('update', Asset::class)) ? true : false,
'audit' => Gate::allows('audit', Asset::class) ? true : false,
'delete' => ($asset->deleted_at=='' && $asset->assigned_to =='' && Gate::allows('delete', Asset::class) && ($asset->deleted_at == '')) ? true : false,
];
@@ -202,6 +203,7 @@ class AssetsTransformer
'last_name'=> ($asset->assigned->last_name) ? e($asset->assigned->last_name) : null,
'email'=> ($asset->assigned->email) ? e($asset->assigned->email) : null,
'employee_number' => ($asset->assigned->employee_num) ? e($asset->assigned->employee_num) : null,
'jobtitle' => $asset->assigned->jobtitle ? e($asset->assigned->jobtitle) : null,
'type' => 'user',
] : null;
}
@@ -302,30 +304,32 @@ class AssetsTransformer
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
{
if ($accessory_checkout->accessory) {
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'assigned_to' => $accessory_checkout->assigned_to,
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/' . e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int)$accessory_checkout->adminuser->id,
'name' => e($accessory_checkout->adminuser->present()->fullName),
] : null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($accessory_checkout->deleted_at, 'datetime'),
];
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'assigned_to' => $accessory_checkout->assigned_to,
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int) $accessory_checkout->adminuser->id,
'name'=> e($accessory_checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$array += $permissions_array;
return $array;
$array += $permissions_array;
return $array;
}
}
}

View File

@@ -4,6 +4,10 @@ namespace App\Http\Transformers;
class DatatablesTransformer
{
/**
* Transform data for bootstrap tables and API responses for lists of things
**/
public function transformDatatables($objects, $total = null)
{
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
@@ -11,4 +15,15 @@ class DatatablesTransformer
return $objects_array;
}
}
/**
* Transform data for returning the status of items within a bulk action
**/
public function transformBulkResponseWithStatusAndObjects($objects, $total)
{
(isset($total)) ? $objects_array['total'] = $total : $objects_array['total'] = count($objects);
$objects_array['rows'] = $objects;
return $objects_array;
}
}

View File

@@ -26,11 +26,11 @@ class DepreciationsTransformer
$array = [
'id' => (int) $depreciation->id,
'name' => e($depreciation->name),
'months' => $depreciation->months.' '.trans('general.months'),
'months' => trans_choice('general.months_plural', $depreciation->months),
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
'assets_count' => $depreciation->assets_count,
'models_count' => $depreciation->models_count,
'licenses_count' => $depreciation->licenses_count,
'assets_count' => ($depreciation->assets_count > 0) ? (int) $depreciation->assets_count : 0,
'models_count' => ($depreciation->models_count > 0) ? (int) $depreciation->models_count : 0,
'licenses_count' => ($depreciation->licenses_count > 0) ? (int) $depreciation->licenses_count : 0,
'created_by' => ($depreciation->adminuser) ? [
'id' => (int) $depreciation->adminuser->id,
'name'=> e($depreciation->adminuser->present()->fullName()),

View File

@@ -24,7 +24,7 @@ class GroupsTransformer
$array = [
'id' => (int) $group->id,
'name' => e($group->name),
'permissions' => json_decode($group->permissions),
'permissions' => $group->decodePermissions(),
'users_count' => (int) $group->users_count,
'notes' => Helper::parseEscapedMarkedownInline($group->notes),
'created_by' => ($group->adminuser) ? [

View File

@@ -2,6 +2,7 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\License;
use App\Models\LicenseSeat;
use Illuminate\Support\Facades\Gate;
@@ -12,20 +13,20 @@ class LicenseSeatsTransformer
public function transformLicenseSeats(Collection $seats, $total)
{
$array = [];
$seat_count = 0;
foreach ($seats as $seat) {
$seat_count++;
$array[] = self::transformLicenseSeat($seat, $seat_count);
$array[] = self::transformLicenseSeat($seat);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformLicenseSeat(LicenseSeat $seat, $seat_count = 0)
public function transformLicenseSeat(LicenseSeat $seat)
{
$array = [
'id' => (int) $seat->id,
'license_id' => (int) $seat->license->id,
'updated_at' => Helper::getFormattedDateObject($seat->updated_at, 'datetime'), // we use updated_at here because the record gets updated when it's checked in or out
'assigned_user' => ($seat->user) ? [
'id' => (int) $seat->user->id,
'name'=> e($seat->user->present()->fullName),
@@ -36,24 +37,23 @@ class LicenseSeatsTransformer
'name' => e($seat->user->department->name),
] : null,
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
] : null,
'assigned_asset' => ($seat->asset) ? [
'id' => (int) $seat->asset->id,
'name'=> e($seat->asset->present()->fullName),
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
] : null,
'location' => ($seat->location()) ? [
'id' => (int) $seat->location()->id,
'name'=> e($seat->location()->name),
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
] : null,
'reassignable' => (bool) $seat->license->reassignable,
'notes' => e($seat->notes),
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
];
if ($seat_count != 0) {
$array['name'] = trans('admin/licenses/general.seat_count', ['count' => $seat_count]);
}
$permissions_array['available_actions'] = [
'checkout' => Gate::allows('checkout', License::class),
'checkin' => Gate::allows('checkin', License::class),

View File

@@ -57,6 +57,10 @@ class LocationsTransformer
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
'notes' => Helper::parseEscapedMarkedownInline($location->notes),
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
'created_by' => $location->adminuser ? [
'id' => (int) $location->adminuser->id,
'name'=> e($location->adminuser->present()->fullName),
]: null,
'updated_at' => Helper::getFormattedDateObject($location->updated_at, 'datetime'),
'parent' => ($location->parent) ? [
'id' => (int) $location->parent->id,

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Database\Eloquent\Collection;
class ProfileTransformer
{
public function transformFiles(Collection $files, $total)
{
$array = [];
foreach ($files as $file) {
$array[] = self::transformFile($file);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformFile(Actionlog $file)
{
$array = [
'id' => (int) $file->id,
'icon' => Helper::filetype_icon($file->filename),
'item' => ($file->item) ? [
'name' => ($file->itemType()=='user') ? e($file->item->getFullNameAttribute()) : e($file->item->getDisplayNameAttribute()),
'type' => e($file->itemType()),
] : null,
'filename' => e($file->filename),
'signature_file' => ($file->accept_signature) ? route('profile.signature.view', ['filename' => $file->accept_signature ]) : null,
'note' => e($file->note),
'url' => route('profile.storedeula.download', ['filename' => $file->filename]),
'file' => route('profile.storedeula.download', ['filename' => $file->filename]),
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
];
return $array;
}
}

View File

@@ -45,6 +45,10 @@ class SuppliersTransformer
'components_count' => (int) $supplier->components_count,
'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedownInline($supplier->notes) : null,
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
'created_by' => $supplier->adminuser ? [
'id' => (int) $supplier->adminuser->id,
'name'=> e($supplier->adminuser->present()->fullName),
]: null,
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
];

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Models\Actionlog;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
class UploadedFilesTransformer
{
public function transformFiles(Collection $files, $total)
{
$array = [];
foreach ($files as $file) {
$array[] = self::transformFile($file);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformFile(Actionlog $file)
{
$snipeModel = $file->item_type;
$array = [
'id' => (int) $file->id,
'icon' => Helper::filetype_icon($file->filename),
'name' => e($file->filename),
'item' => ($file->item_type) ? [
'id' => (int) $file->item_id,
'type' => strtolower(class_basename($file->item_type)),
] : null,
'filename' => e($file->filename),
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
'url' => $file->uploads_file_url(),
'note' => ($file->note) ? e($file->note) : null,
'created_by' => ($file->adminuser) ? [
'id' => (int) $file->adminuser->id,
'name'=> e($file->adminuser->present()->fullName),
] : null,
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
];
$permissions_array['available_actions'] = [
'delete' => (Gate::allows('update', $snipeModel) && ($file->deleted_at == '')),
];
$array += $permissions_array;
return $array;
}
}

Some files were not shown because too many files have changed in this diff Show More