Compare commits

...

534 Commits

Author SHA1 Message Date
snipe 85486fa58c Merge remote-tracking branch 'origin/develop' 2024-08-15 10:53:54 +01:00
snipe 10dc1f1368 Re-pull the accessory checkout
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:51:49 +01:00
snipe 3618df2ec5 Merge remote-tracking branch 'origin/develop' 2024-08-15 10:44:39 +01:00
snipe 8f7bce7aad Re-added UI for checkout to non-users for accessories
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:44:27 +01:00
snipe a201f22fdb Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:36:12 +01:00
snipe 6070d530cf Merge remote-tracking branch 'origin/develop' 2024-08-15 10:35:33 +01:00
snipe 4d66f7c93f Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:35:02 +01:00
snipe 6c0cf94473 Merge remote-tracking branch 'origin/develop' 2024-08-15 10:28:08 +01:00
snipe 381003eeab Cleaned up asset tag field on quickscan checkin
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:27:31 +01:00
snipe c3d4c821f3 Merge pull request #15303 from snipe/features/#15301_optional_status_to_quickscan_checkin
Fixed #15301 - Added optional status to quickscan checkin
2024-08-15 10:26:04 +01:00
snipe d8038ac483 Removed status label list
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:17:08 +01:00
snipe d268f25f16 Make asset tag required
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:16:57 +01:00
snipe 01c69c8f8f Allow optional status label on quickscan checkin
Signed-off-by: snipe <snipe@snipe.net>
2024-08-15 10:08:48 +01:00
snipe 3e29457094 Merge pull request #15292 from snipe/features/updated_phpinsights
Updated phpinsights
2024-08-14 12:12:40 +01:00
snipe 0b3ac2a9cd Updated phpinsights
Signed-off-by: snipe <snipe@snipe.net>
2024-08-14 12:08:49 +01:00
snipe bf4b915168 Merge remote-tracking branch 'origin/develop' 2024-08-14 10:45:13 +01:00
snipe 0f1e5181a6 Nicer input layout for bulk edit name
Signed-off-by: snipe <snipe@snipe.net>
2024-08-14 10:09:14 +01:00
snipe 0a9c2e354f Merge remote-tracking branch 'origin/develop' 2024-08-14 10:02:43 +01:00
snipe e544007185 Merge pull request #15291 from snipe/features/bulk_update_asset_name
Added asset name to bulk asset edit
2024-08-14 10:02:09 +01:00
snipe 4b690e8e1f Updated test to check for nulling
Signed-off-by: snipe <snipe@snipe.net>
2024-08-14 10:01:56 +01:00
snipe 371afde52d Bulk edit asset name
Signed-off-by: snipe <snipe@snipe.net>
2024-08-14 09:52:25 +01:00
snipe 7a6a2e3d92 Merge pull request #15290 from marcusmoore/test-fixes
Fixed component check in test
2024-08-14 00:00:55 +01:00
Marcus Moore 3f8ac59f4c Reference the entry in the components_assets table 2024-08-13 15:31:16 -07:00
snipe 74e7b1cfa5 Merge pull request #15286 from marcusmoore/test-updates
Implemented artisan test case
2024-08-13 20:46:46 +01:00
Marcus Moore ae0a6d66f3 Update tests
https://masteringlaravel.io/daily/2024-02-09-watch-out-for-this-when-testing-artisan-commands
2024-08-13 12:16:08 -07:00
Marcus Moore 321839e074 Fix namespace 2024-08-13 12:14:56 -07:00
snipe e9fc455a30 Merge pull request #15192 from Godmartinz/status_labels_check_emails
Added status labels to confirmation emails
2024-08-13 18:24:15 +01:00
Godfrey M 8af4126de1 removed indent from blades 2024-08-13 10:21:33 -07:00
snipe 47f1b3b14e Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2024-08-13 16:48:06 +01:00
snipe 3044af9410 Merge pull request #15280 from snipe/bug/sc-23294
Fixed select2 “x” color on multi-select and border color
2024-08-13 16:46:56 +01:00
snipe 4f0f0af83b Make the border radius match the rest of the site
Signed-off-by: snipe <snipe@snipe.net>
2024-08-13 16:46:26 +01:00
snipe 198afee946 Fixed select2 “x” color on mult-select and border color
Signed-off-by: snipe <snipe@snipe.net>
2024-08-13 16:42:23 +01:00
snipe def0af652d Merge pull request #15279 from TelecomsSansFrontieres/fix_missing-translate-item-assets
Fix: add missing admin/hardware/table.name key
2024-08-13 16:35:25 +01:00
Florent Bervas 20e4ba0590 lang: relocated translation key to existing item in general 2024-08-13 15:07:54 +00:00
snipe 72fd9977e5 Merge pull request #15277 from uberbrady/silence_saml_errors
Fixed: [sc-26355] Attempt to de-escalate SAML login and logout errors
2024-08-13 15:51:03 +01:00
Brady Wetherington 10f35c682b Re-add space 2024-08-13 15:49:51 +01:00
snipe 198b76ebc2 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2024-08-13 15:47:00 +01:00
snipe 6e84c29ce4 Merge pull request #15278 from snipe/print_view_improvements
Fixed #14821 - Hide UI elements from print view on assets
2024-08-13 15:45:30 +01:00
snipe 389a06aac5 Hide UI elements from print view on assets
Signed-off-by: snipe <snipe@snipe.net>
2024-08-13 15:40:51 +01:00
Brady Wetherington 4b96721393 Attempt to de-escalate SAML login and logout errors 2024-08-13 14:55:13 +01:00
snipe e42ee0c0e1 Merge pull request #15276 from snipe/css_updates
Bold selected tab for better readability
2024-08-13 14:21:45 +01:00
snipe 277a0fd16a Make selected tab bold
Signed-off-by: snipe <snipe@snipe.net>
2024-08-13 14:19:41 +01:00
snipe c163d6774f Merge pull request #15198 from spencerrlongg/feature/add_trait_to_request
Add MayContainCustomFields Trait to Asset Update Request
2024-08-13 14:12:52 +01:00
snipe 5546f9676a Merge remote-tracking branch 'origin/develop' 2024-08-13 13:51:33 +01:00
snipe 9e73eaf955 Merge pull request #15275 from snipe/added_tooltips_to_top_bar
Added tooltips to top nav shortcuts
2024-08-13 13:50:27 +01:00
snipe 1b2094f4e2 Added tooltips to top nav shortcuts
Signed-off-by: snipe <snipe@snipe.net>
2024-08-13 13:49:13 +01:00
snipe 46caa478d2 Merge remote-tracking branch 'origin/develop' 2024-08-12 20:47:45 +01:00
snipe e6106aa7cf Merge pull request #15272 from snipe/fixes/added_selectlist_permission_for_reports
Added reporting-only access for selectlists
2024-08-12 20:47:17 +01:00
snipe b45a8f4b5f Added reporting-only access for selectlists
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 20:44:17 +01:00
snipe 4d8569e754 Merge remote-tracking branch 'origin/develop' 2024-08-12 18:01:21 +01:00
snipe c9f8a84d48 Merge pull request #15270 from snipe/fixes/make_dashboard_pie_respect_show_in_list
Fixed #15247 - make pie chart on dashboard respect the “show archived…
2024-08-12 17:59:27 +01:00
snipe ac1543d568 Fixed #15247 - make pie chart on dashboard respect the “show archived” setting
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 17:54:42 +01:00
snipe 4dc57f95e2 Merge remote-tracking branch 'origin/develop' 2024-08-12 16:38:23 +01:00
snipe 9d354ca657 Merge pull request #15266 from uberbrady/checkout_to_fixer
Checkout-to fixer
2024-08-12 16:34:19 +01:00
snipe 316ee6f810 Merge remote-tracking branch 'origin/develop' 2024-08-12 16:30:53 +01:00
snipe 3f2139349e Allow better fallthrough on image
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 16:30:21 +01:00
snipe f56dd582f9 Merge pull request #15269 from snipe/fixes/smaller_email_logo
Fixes ##15236 - smaller email logo
2024-08-12 16:27:59 +01:00
snipe 225ab47b8e Simplified logic
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 16:23:09 +01:00
snipe 4c350bd5a3 Remove the regular logo fallback, make image capped by height
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 16:21:58 +01:00
snipe 509cc812ea Merge remote-tracking branch 'origin/develop' 2024-08-12 15:58:40 +01:00
snipe e7fb29fced Fixed copy area for serial
Signed-off-by: snipe <snipe@snipe.net>
2024-08-12 15:58:30 +01:00
Brady Wetherington a2d11b43de Whoops, these were accidentally committed 2024-08-12 13:50:46 +01:00
Brady Wetherington f80cf666b1 Mark tests as incomplete for the new artisan assigned_to fixer 2024-08-12 13:46:56 +01:00
snipe d5f66bc0dd Merge remote-tracking branch 'origin/develop' 2024-08-12 10:10:59 +01:00
snipe ce5be8ac24 Merge pull request #15100 from Godmartinz/depreciation_percentage
Added Depreciation Percentage as an option
2024-08-12 10:10:25 +01:00
snipe 1e213c31ef Merge remote-tracking branch 'origin/develop' 2024-08-11 10:26:16 +01:00
snipe 1777bb4b93 Pulled searchable relation on model - needs revisit
Signed-off-by: snipe <snipe@snipe.net>
2024-08-11 10:26:02 +01:00
snipe 91874dfe0b Merge remote-tracking branch 'origin/develop' 2024-08-10 19:37:37 +01:00
snipe 5c6c655cdc Added sounds to quickscan checkin
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 19:37:18 +01:00
snipe 0e6eda5e8a Merge remote-tracking branch 'origin/develop' 2024-08-10 18:29:41 +01:00
snipe 60eb602156 Merge pull request #15264 from snipe/fixes/fixed_admin_ordering_on_report
Fixed #15252 - admin ordering on activity report
2024-08-10 18:26:15 +01:00
snipe 24560b9362 Updated prod assets
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 18:24:49 +01:00
snipe 76b114d820 Added test
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 18:21:41 +01:00
snipe ce33deed86 Remove unused use statement
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:14:34 +01:00
snipe 1e6c172f18 Removed unusued use statement
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:14:20 +01:00
snipe a1ce9b9285 Added query scope for ordering by admin
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:00:50 +01:00
snipe 6ec3693030 Load admin on actionlog
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:00:34 +01:00
snipe 71729f0b16 Load admin and model on assets
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:00:20 +01:00
snipe 1c229a8e08 Made notes field sortable
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 17:00:11 +01:00
snipe 07b1e3fb90 Fixed tests
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 16:55:48 +01:00
snipe 6294516271 Merge pull request #15263 from snipe/really/remove_pound_sign_for_order_number
Fixed #15248 Removed pound symbol from order number on asset view
2024-08-10 16:18:40 +01:00
snipe f3331b5045 Removed pound symbol from order number on asset view
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 16:17:07 +01:00
snipe 6a06a91fa2 Merge pull request #15262 from snipe/synk/jquery-validation-from-1.20.1-to-1.21.0
Upgrade jquery-validation from 1.20.1 to 1.21.0
2024-08-10 16:07:47 +01:00
snipe 0cba5ba67e Upgrade jquery-validation from 1.20.1 to 1.21.0
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 16:06:14 +01:00
snipe b815352a45 Merge pull request #15261 from snipe/security/upgrade_fontawesome
[Snyk] Upgrade @fortawesome/fontawesome-free from 6.5.2 to 6.6.0
2024-08-10 15:57:10 +01:00
snipe 2f299dbb20 Upgrade fontawesome, generate dev assets
Signed-off-by: snipe <snipe@snipe.net>
2024-08-10 15:51:24 +01:00
snipe f76fbe75e0 Merge pull request #15221 from marcusmoore/fixes/bulk-checkin-logging
Fixed accessories and consumables not being logged correctly during bulk check-in
2024-08-08 10:43:59 +01:00
snipe cc1e356c35 Merge pull request #15232 from marcusmoore/shift-126036
Updated PHPUnit to v10
2024-08-08 01:23:07 +01:00
snipe 8094f3c0e1 Merge pull request #15245 from spencerrlongg/bug/acceptance_issue
Fixed Issue with Acceptances Being Created
2024-08-08 01:22:34 +01:00
snipe 900beaa955 Merge pull request #15244 from snipe/features/play_sound_on_audit
Fixes #10560 - optional ability to play sound on audit
2024-08-08 00:58:30 +01:00
spencerrlongg 2412333152 added conditional to listener 2024-08-07 16:53:06 -05:00
Marcus Moore 4a2d2f7336 Improve variable name 2024-08-07 13:32:30 -07:00
Marcus Moore d03baa4613 Make diff cleaner 2024-08-07 13:29:45 -07:00
Marcus Moore 41437e4d8f Merge branch 'develop' into fixes/bulk-checkin-logging
# Conflicts:
#	app/Http/Controllers/Users/BulkUsersController.php
2024-08-07 13:06:59 -07:00
snipe 7d220dd8c2 set default
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:58:04 +01:00
snipe a99ec062e6 Added checkbox for preferences
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:49:07 +01:00
snipe 8fe6395287 Added migration
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:41:13 +01:00
snipe b4d51599ee Added sound effects
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:38:50 +01:00
snipe 9350a20189 Include accessories count
This isn’t exactly right right now

Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:19:47 +01:00
snipe 889d5da71e Check for accessories on bulk items
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 20:04:32 +01:00
snipe 5546ab08c2 Only delete user records in accessories_checkouts for bulk delete
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 19:53:18 +01:00
snipe ef183f256c Merge pull request #15242 from Godmartinz/shortcut_option
Fixed shortcuts to be optional now
2024-08-07 18:56:27 +01:00
Godfrey M 3a96e501cc use old school input 2024-08-07 10:54:06 -07:00
Godfrey M 6b1784f2ee makes shortcuts optional 2024-08-07 10:47:16 -07:00
snipe 1a872b1643 Merge remote-tracking branch 'origin/develop' 2024-08-07 18:15:44 +01:00
snipe deef9ad181 Merge pull request #15241 from snipe/partial_revert_for_checkout_accessories_to_non_user
Partial revert for checkout accessories to non user
2024-08-07 18:15:08 +01:00
snipe 0dc7e8b8dc Temp removed other selector
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 18:04:52 +01:00
snipe a5f075548a Default to user
Signed-off-by: snipe <snipe@snipe.net>
2024-08-07 18:04:39 +01:00
Brady Wetherington 1706ddd511 working fixup script, but failing tests. But I have tests! 2024-08-07 16:06:00 +01:00
Marcus Moore a220986d16 Switch to snake case 2024-08-06 16:13:51 -07:00
Marcus Moore 2a8ac81eed Fix namespace 2024-08-06 15:22:13 -07:00
Marcus Moore d3cb3c03d2 Rerun composer update using php 8.1 2024-08-06 15:01:01 -07:00
Marcus Moore c60bc5cdbe Change indents back to 2 2024-08-06 13:51:44 -07:00
Marcus Moore 6cfbf835c7 Migrate phpunit.xml
vendor/bin/phpunit --migrate-configuration
2024-08-06 13:37:53 -07:00
Marcus Moore 047b77e038 Composer update for phpunit, collision, and paratest
composer update phpunit/phpunit nunomaduro/collision brianium/paratest --with-all-dependencies
2024-08-06 13:34:51 -07:00
snipe 766b370264 Merge pull request #15229 from marcusmoore/bug/sc-26552
Disallowed checking out components to different companies and fixed number remaining counts
2024-08-06 21:33:55 +01:00
snipe da33277234 Merge pull request #15230 from marcusmoore/fixes/test-namespaces
Fixed handful of test namespaces
2024-08-06 21:32:11 +01:00
Marcus Moore 3e832e5e94 Revert "Define test classes as final"
This reverts commit 95516b0343.
2024-08-06 13:31:17 -07:00
Marcus Moore 82e795b642 Revert "Add return types to test methods"
This reverts commit 83fb6826ee.
2024-08-06 13:30:34 -07:00
Shift 95516b0343 Define test classes as final 2024-08-06 20:25:22 +00:00
Shift 83fb6826ee Add return types to test methods 2024-08-06 20:25:22 +00:00
Shift b1e92ab866 Declare data providers as static 2024-08-06 20:25:21 +00:00
Shift 96241cb67c Adopt PHP attributes in test classes 2024-08-06 20:25:21 +00:00
Shift b680dab5c9 Ignore PHPUnit cache folder 2024-08-06 20:25:19 +00:00
Shift 984840dc82 Bump PHPUnit dependencies 2024-08-06 20:25:18 +00:00
Marcus Moore 84e447af09 Fix test namespaces 2024-08-06 13:21:36 -07:00
Marcus Moore c7ddabcc8b Check for FMCS when checking out component 2024-08-06 12:27:15 -07:00
Marcus Moore bee80fcf8a Remove global scope when counting check outs 2024-08-06 12:16:06 -07:00
Marcus Moore 0aff35b622 Scaffold additional failed tests 2024-08-06 12:07:34 -07:00
Godfrey M 46a6a84ecb adds migration and translations 2024-08-06 11:07:39 -07:00
Marcus Moore 374812f8fe Add failing test 2024-08-06 10:48:38 -07:00
Marcus Moore 94e00b8a3e Use new accessory checkout relationship 2024-08-05 17:26:11 -07:00
Marcus Moore f2b78d18a4 Merge branch 'develop' into fixes/bulk-checkin-logging
# Conflicts:
#	app/Http/Controllers/Users/BulkUsersController.php
2024-08-05 17:25:54 -07:00
Marcus Moore 17eccfcd8b Formatting 2024-08-05 16:58:27 -07:00
Marcus Moore 01e4382d20 Formatting 2024-08-05 16:58:17 -07:00
Marcus Moore 1c664af326 Remove todo outside of scope 2024-08-05 16:58:08 -07:00
Marcus Moore 392d34422a Remove code handled by ConsumableAssignment:: call above 2024-08-05 16:57:40 -07:00
Marcus Moore 35e7a3163c Implement test case 2024-08-05 16:54:31 -07:00
Marcus Moore 96fafa6952 Improve readability 2024-08-05 16:36:29 -07:00
Marcus Moore a55693211f Move test class 2024-08-05 16:27:04 -07:00
Marcus Moore 1acd24fdbe Re-order helpers 2024-08-05 16:26:25 -07:00
Marcus Moore 59b7d5db4b Remove comment 2024-08-05 16:25:14 -07:00
Marcus Moore 16aa47509b Implement test for assets 2024-08-05 16:24:52 -07:00
Marcus Moore d1bb3ef6bf Scaffold two tests 2024-08-05 16:19:09 -07:00
Marcus Moore 5cd9dd4a67 Add assertion to ensure user cannot perform bulk actions on self 2024-08-05 16:17:24 -07:00
Marcus Moore fc405d9d73 Assert redirected to correct place 2024-08-05 16:14:01 -07:00
Marcus Moore 5786ff5035 Add assertion for success message 2024-08-05 16:11:07 -07:00
Marcus Moore 8a206a6d92 Add assertion 2024-08-05 16:10:12 -07:00
Marcus Moore 67b3ab820f Add todo comments 2024-08-05 16:10:08 -07:00
Marcus Moore b06501dd02 Add assertions 2024-08-05 16:02:59 -07:00
Marcus Moore e3049fffd4 Remove comment 2024-08-05 15:50:53 -07:00
Marcus Moore 5ecdb7e07c Add validation test 2024-08-05 15:50:09 -07:00
Marcus Moore 4ed3347f52 Add permission check 2024-08-05 15:43:38 -07:00
Marcus Moore 364775dcfe Improve readability 2024-08-05 15:42:03 -07:00
Marcus Moore bfebcdc7ed Improve variable name 2024-08-05 15:22:35 -07:00
Marcus Moore 480e4f3a69 Improve readability 2024-08-05 15:16:40 -07:00
Marcus Moore 78a0417ee9 Add another user into the mix 2024-08-05 15:11:18 -07:00
snipe b83d148b37 Merge pull request #15217 from TelecomsSansFrontieres/feature_extend-search-by-tag
Feature: Extend search capabilities to other assets attributes
2024-08-05 15:38:51 +01:00
Florent Bervas ad794248fe add a warning message if asset tag not found 2024-08-05 13:54:33 +00:00
Florent Bervas b804791ff6 feature: extend search capabilities to other assets attributes 2024-08-05 12:26:29 +00:00
snipe c45bf870b7 Merge pull request #15211 from snipe/feaures/15205_copy_asset_tag
Fixed #15205 - adds copy to asset tag
2024-08-02 20:59:53 +01:00
snipe a0bd8b6049 Fixed #15205 - adds copy to asset tag
Signed-off-by: snipe <snipe@snipe.net>
2024-08-02 20:56:31 +01:00
Godfrey M ae4f278df1 edited terenaries on notifs 2024-08-01 11:13:20 -07:00
snipe 28abb8d8cc Hotfix for 405 update asset
Signed-off-by: snipe <snipe@snipe.net>
2024-08-01 12:30:35 +01:00
snipe 24d948a3f6 Merge pull request #15204 from snipe/fixes/405_on_api_asset_update
Fixed (temp) put route
2024-08-01 12:28:23 +01:00
snipe c1c9c3554e Fixed (temp) put route
Signed-off-by: snipe <snipe@snipe.net>
2024-08-01 12:27:02 +01:00
snipe fcea564afa Fixed button label
Signed-off-by: snipe <snipe@snipe.net>
2024-07-31 21:44:27 +01:00
snipe f44abd0b28 Fixed button label
Signed-off-by: snipe <snipe@snipe.net>
2024-07-31 21:43:09 +01:00
Godfrey M 854903805b one more spot 2024-07-31 12:16:41 -07:00
Godfrey M 868f117b67 Merge branch 'status_labels_check_emails' of github.com:Godmartinz/snipe-it into status_labels_check_emails 2024-07-31 12:03:56 -07:00
Godfrey M b6cac4baae corrects ? usage 2024-07-31 12:03:36 -07:00
snipe 4fcb3df1d9 Merge pull request #15199 from Godmartinz/fix-signature-pad
adds closing brackets to signature pad
2024-07-31 19:23:33 +01:00
Godfrey M b60e22bcb4 adds closing brackets to signature pad 2024-07-31 11:10:33 -07:00
spencerrlongg 61312c2eec oops, mysql 2024-07-31 12:07:50 -05:00
spencerrlongg b0063b1d4a test written 2024-07-31 11:57:35 -05:00
akemidx fffcbdc44d Merge remote-tracking branch 'upstream/develop' into upstream/dev 2024-07-31 07:14:06 -04:00
akemidx 8d1fa362f7 restoring code 2024-07-31 07:13:42 -04:00
snipe 31a2765b30 Merge pull request #15145 from Godmartinz/purge-storage
Added user storage files to purge command
2024-07-31 10:16:08 +01:00
snipe e79a5b7efe Merge pull request #15193 from Godmartinz/admin-settings
Fixed admin boxes alignment
2024-07-31 08:54:44 +01:00
snipe ca531e85f3 Merge pull request #15194 from spencerrlongg/bug/sc-26247
Fixed A Translation Issue With Auth
2024-07-31 08:54:09 +01:00
spencerrlongg 0941c0944a ok, found issue, but need to test some things now... 2024-07-30 22:55:15 -05:00
spencerrlongg 9b80843c77 tests a little broken, added some nullchecks 2024-07-30 21:44:22 -05:00
spencerrlongg 8684a3efc3 delete note, add trait to other request 2024-07-30 21:22:46 -05:00
akemidx fccfce2ae8 first thought 2024-07-30 19:27:37 -04:00
Godfrey M 935d2ce29a fixes admin box alignment 2024-07-30 15:05:09 -07:00
spencerrlongg 437ddc01b4 removed extraneous $request->validate() arguments 2024-07-30 16:26:24 -05:00
Godfrey Martinez f19e58b352 Merge pull request #21 from Godmartinz/status_labels_check_emails_checkout
adds Status labels to check in and out emails
2024-07-30 09:41:05 -07:00
Godfrey M eb6f330e67 adds status to check in and out 2024-07-30 09:38:39 -07:00
Godfrey M f19899543d changed error to info 2024-07-30 09:18:17 -07:00
snipe 3a2611f8e1 Merge pull request #14565 from spencerrlongg/bug/sc-23936
Checks that custom fields exist before saving
2024-07-30 11:36:55 +01:00
Godfrey M 53ad312700 added missing closing bracket 2024-07-29 15:17:32 -07:00
Godfrey M 89d375daad add indents 2024-07-29 15:15:41 -07:00
Godfrey M d46f9776fe remove text 2024-07-29 15:14:58 -07:00
Godfrey M f9a47c8a9f adds a try catch 2024-07-29 15:12:48 -07:00
Godfrey M e395ee1878 adds a try catch 2024-07-29 15:12:32 -07:00
snipe 4971c54b05 Fixed seeder
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 20:17:25 +01:00
snipe 4c5b82ae37 Merge pull request #15188 from Godmartinz/1d_barcode_select_bug
Fixed 1d barcode targeting.
2024-07-29 20:00:21 +01:00
Spencer Long fd7082c30f Merge branch 'develop' into bug/sc-23936 2024-07-29 14:00:00 -05:00
snipe 8d0b72293f Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2024-07-29 18:51:19 +01:00
snipe f3d98f90c0 Add @arne-kroeger as a contributor 2024-07-29 18:51:12 +01:00
snipe 4eccb5ffc6 Merge pull request #15185 from arne-kroeger/feat/accesspories-checkout-to-location-or-asset
Added #14979: add checkout to location and assets functionality to accessories
2024-07-29 18:50:48 +01:00
Godfrey M 86049624c7 retargeted new label engine to correct 1d column 2024-07-29 10:42:26 -07:00
snipe 3972782033 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2024-07-29 18:27:56 +01:00
snipe 33b86057d1 Version bump
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 18:27:01 +01:00
arne-kroeger 3c3b922eae Adjusted newly added tests to new checkout form 2024-07-29 19:15:01 +02:00
arne-kroeger e8d0147075 Adjusted missing down on migration and code smells 2024-07-29 19:04:10 +02:00
snipe c2bcc2e2d9 Merge remote-tracking branch 'origin/develop' 2024-07-29 14:39:13 +01:00
snipe 55f9886412 Merge pull request #15187 from snipe/feature/sc-26458
Use the `pwd_secure_min` value (plus 5) for generated password
2024-07-29 14:38:39 +01:00
snipe a77ece01a6 Fixed test name
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 14:34:10 +01:00
snipe 1c1101aeac Use the pwd_secure_min value (plus 5) for generated password
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 14:26:18 +01:00
snipe de04ead55d Merge remote-tracking branch 'origin/develop' 2024-07-29 10:11:39 +01:00
snipe bc18fac97e Fixed missing div
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 10:11:11 +01:00
arne-kroeger 3a0b03348e added additional tests 2024-07-29 11:06:36 +02:00
arne-kroeger b18baf74d2 added options to checkout accessoires to locations and assets
Added #14979: add checkout functionality to accessoires
2024-07-29 10:54:53 +02:00
snipe 28c7355697 Fixed missing div
Signed-off-by: snipe <snipe@snipe.net>
2024-07-29 09:52:38 +01:00
snipe 0dd0baa511 Merge remote-tracking branch 'origin/develop' 2024-07-27 10:12:23 +01:00
snipe c3a296c19f Added qr code back
Signed-off-by: snipe <snipe@snipe.net>
2024-07-27 10:12:13 +01:00
snipe f6cc923e01 Merge remote-tracking branch 'origin/develop' 2024-07-26 17:29:46 +01:00
snipe aab29fbb6b Use translated string for restore
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 16:47:40 +01:00
snipe d9d3ff27ae Merge remote-tracking branch 'origin/develop' 2024-07-26 15:31:52 +01:00
snipe 614d05acb5 Merge pull request #15170 from uberbrady/simplify_css_skin_building
Dynamically iterate through the skin listing to build skins
2024-07-26 15:14:24 +01:00
snipe e4a82edd3f Merge pull request #15172 from snipe/features/blade_component_for_submit
Use blade component for submit redirect on asset edit/create
2024-07-26 15:13:30 +01:00
snipe eceaa72781 And more tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 15:07:43 +01:00
snipe ddf45c5ee9 Renamed test
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 15:00:16 +01:00
snipe ff100c0b9f Added more tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 14:58:10 +01:00
snipe 037cc4d098 More tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 14:27:07 +01:00
snipe ff6e6ef88c Added more tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 13:43:10 +01:00
snipe e8ec11652f Very basic checkin tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 13:16:31 +01:00
snipe cd7f276c40 Fixed logging for component
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 13:09:21 +01:00
snipe fc8bb82a02 Made dropdown wider
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 12:48:18 +01:00
snipe 5d7f1f77a3 Added redirect to checkin/checkout controllers
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 12:48:07 +01:00
snipe 5dea3f4495 Added blade component to checkin/checkout pages
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 12:47:33 +01:00
snipe c6e709cd36 Added parameters
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 12:45:06 +01:00
Brady Wetherington daf5a80081 Use a similar loop for the minification step for CSS 2024-07-26 12:03:03 +01:00
snipe 759ab78f80 Added more redirects
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 11:27:36 +01:00
snipe b6d9f736e3 Pulled use session
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 10:41:28 +01:00
snipe 243ab51def Fixed back button
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 10:40:48 +01:00
snipe c5603a0e10 Merge remote-tracking branch 'origin/develop' 2024-07-26 10:26:23 +01:00
snipe 57f6ecb1da Added created_by to license view
Signed-off-by: snipe <snipe@snipe.net>
2024-07-26 10:24:13 +01:00
snipe 64082ada1e Updated test
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 22:08:51 +01:00
snipe 068535a80c Removed logging
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 22:08:46 +01:00
snipe 08f4fe5f35 Add status messages to redirect
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 22:02:34 +01:00
snipe 018b5684fc Refactor helper method for redirection
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 22:02:03 +01:00
snipe 73a80a5fbc Use blade component for redirect option
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 21:35:13 +01:00
Brady Wetherington 4c8bf9ae19 Dynamically iterate through the skin listing to build skins 2024-07-25 21:20:06 +01:00
snipe f77d300549 Merge pull request #15169 from uberbrady/create_directories_when_needed_on_restore
Create intermediate directories on restore if needed - Fixes [SC-25950]
2024-07-25 18:45:54 +01:00
snipe 62655be2d0 Fixed BYOD label
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 18:41:37 +01:00
Brady Wetherington eb938bdba3 Create intermediate directories on restore if needed 2024-07-25 18:40:26 +01:00
snipe 129d3b35fb Merge pull request #15168 from uberbrady/improve_restore_sanitization
Improve restore sanitization - Fixes [sc-24840]
2024-07-25 18:20:05 +01:00
Brady Wetherington 84df23e1f6 Better handle older SQL dumps that got created in Windows format 2024-07-25 18:07:25 +01:00
snipe a439d8abe8 Use fully qualified use statements
Signed-off-by: snipe <snipe@snipe.net>
2024-07-25 16:08:46 +01:00
snipe 24b7659c23 Merge pull request #15150 from marcusmoore/chore/sc-26113
Registered custom anonymous blade component directory
2024-07-25 16:08:01 +01:00
Marcus Moore 44dbbeb608 Add accessory and consumable specific checkin methods 2024-07-24 14:17:49 -07:00
Marcus Moore 7161b6416e Add failing test for accessories and consumables checkin 2024-07-24 14:16:42 -07:00
snipe 1331bffaca Merge remote-tracking branch 'origin/develop' 2024-07-24 21:42:41 +01:00
snipe 9b422e5c97 Baremetrics BMPay breaks with CSP turned [sc-25011]
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 21:42:20 +01:00
snipe 0c86855392 Merge remote-tracking branch 'origin/develop' 2024-07-24 21:04:20 +01:00
snipe 962ae5231d Merge pull request #15162 from snipe/fixes/added_location_assets_endpoint
Added assets endpoint for locations
2024-07-24 21:03:43 +01:00
snipe 590d13061a One more test
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:57:26 +01:00
snipe c3a2cdeee9 Added assets endpoint for locations
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:53:58 +01:00
snipe ef77fb91c0 Merge remote-tracking branch 'origin/develop' 2024-07-24 20:20:37 +01:00
snipe 02bd8d7ea1 Merge pull request #15161 from snipe/fixes/500_when_depreciation_is_active_but_no_purchase_date
Fixes 500 when depreciation is active but no purchase date
2024-07-24 20:20:10 +01:00
snipe da4ec145d7 Removed test no longer needed due to validation
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:17:30 +01:00
snipe ef145e47b4 Added tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:13:15 +01:00
snipe adf58a06da Added check for purchase date
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:13:11 +01:00
snipe 63f0b5279d Added check for purchase_date
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 20:13:01 +01:00
Godfrey Martinez c96ccbb6cb Merge branch 'develop' into depreciation_percentage 2024-07-24 12:12:28 -07:00
Godfrey M 950fff31ed fix conflicts 2024-07-24 12:12:04 -07:00
Godfrey M f05d8281f3 fixes alignment for error msg 2024-07-24 12:06:56 -07:00
Godfrey M d5c9fa823e adds translations 2024-07-24 11:39:33 -07:00
Godfrey M 99741db645 adds status labels to confirmation emails 2024-07-24 11:24:30 -07:00
snipe 90d5a6d8ab Merge remote-tracking branch 'origin/develop' 2024-07-24 19:16:56 +01:00
snipe 5b8529bc83 Fixed pluralization
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 19:16:08 +01:00
snipe f76c68f0ed Merge remote-tracking branch 'origin/develop' 2024-07-24 18:10:23 +01:00
snipe 8368fb5c41 Merge pull request #15160 from snipe/fixes/allow_cloning_of_deleted_assets
Allow cloning of deleted assets
2024-07-24 18:09:55 +01:00
snipe d1c39a737f Fixed double semicolon
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 18:09:32 +01:00
snipe 425bfa4318 Added test
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 18:06:21 +01:00
snipe 293abbd1d0 Allow cloning of deleted assets
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 17:49:38 +01:00
snipe b64b774bd5 Use route model binding on clone, removed unneeded route
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 17:49:21 +01:00
snipe 466ab1e3c0 Removed the banner since we already warn
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 17:40:24 +01:00
snipe daa9e7810e Merge pull request #15158 from snipe/fixes/ui_cleanup_for_asset_view
Cleaned up UI on asset view
2024-07-24 17:34:17 +01:00
snipe 95a0f3dbe5 Cleaned up UI on asset view
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 17:26:55 +01:00
snipe e5ab12496f Merge pull request #15151 from snipe/snyk-fix-067da5564c4d29b025cd356b125e6ad9
[Snyk] Security upgrade alpine from 3.18.6 to 3.19
2024-07-24 14:22:57 +01:00
snipe 4b906e39be Merge pull request #15155 from snipe/fix_new_alpine
Switch dockerfile to using php8.2 instead of 8.1 due to Alpine changes
2024-07-24 14:00:21 +01:00
Brady Wetherington 0dc8f27240 Switch dockerfile to using php8.2 instead of 8.1 due to Alpine changes 2024-07-24 13:56:27 +01:00
snipe c4135c71d4 Merge remote-tracking branch 'origin/develop' 2024-07-24 13:05:16 +01:00
snipe 172da4d655 Merge pull request #15154 from snipe/fixes/15103_show_eol_to_users
Fixes #15103 - Added EOL and audit into to user profile assets
2024-07-24 13:03:50 +01:00
snipe 6f8ccd4e10 Fixes #15103 - Added EOL and audit into to user profile assets
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 12:54:33 +01:00
snipe 8d36a75342 Merge remote-tracking branch 'origin/develop' 2024-07-24 12:41:20 +01:00
snipe 9457e5b890 Merge pull request #15153 from snipe/fixes/mail_backup_config
Fixed env var for backup config - related to #14964
2024-07-24 12:40:46 +01:00
snipe bce7a278ae Fixed env var for backup config - related to #14964
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 12:38:58 +01:00
snipe a6041ae525 Merge remote-tracking branch 'origin/develop' 2024-07-24 10:41:36 +01:00
snipe ab1e097b7c Early return for requiredness
Signed-off-by: snipe <snipe@snipe.net>
2024-07-24 10:41:14 +01:00
snipe a781f84aea Merge remote-tracking branch 'origin/develop' 2024-07-24 10:36:23 +01:00
snipe 90a2a808d6 Merge pull request #15149 from spencerrlongg/bug/check_requiredness
Fixed: Requiredness Check Bug
2024-07-24 10:33:45 +01:00
snipe 73e2d8e305 Merge remote-tracking branch 'origin/develop' 2024-07-24 10:32:54 +01:00
snyk-bot 0e1e13f54a fix: Dockerfile.alpine to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE318-BUSYBOX-6913411
- https://snyk.io/vuln/SNYK-ALPINE318-BUSYBOX-7249236
- https://snyk.io/vuln/SNYK-ALPINE318-BUSYBOX-7249265
- https://snyk.io/vuln/SNYK-ALPINE318-BUSYBOX-7249419
- https://snyk.io/vuln/SNYK-ALPINE318-BUSYBOX-7249419
2024-07-24 09:11:01 +00:00
Marcus Moore 869c06f454 Register anonymous blade component namespace 2024-07-23 15:41:58 -07:00
spencerrlongg b61ab423ca ok NOW it should be fine 2024-07-23 17:35:48 -05:00
spencerrlongg 090a595c99 conflict 2024-07-23 17:34:17 -05:00
Spencer Long bc417308c7 Merge branch 'develop' into bug/check_requiredness 2024-07-23 17:32:43 -05:00
spencerrlongg e77e16b9aa this works 2024-07-23 17:22:20 -05:00
spencerrlongg 0ae297634e ugh 2024-07-23 16:59:58 -05:00
spencerrlongg 0f2f559d7a test works, helper still needs work. pushing to check something 2024-07-23 16:46:29 -05:00
snipe 57b5b12952 Merge pull request #15148 from marcusmoore/fixes/dates-in-user-import
Fixed start_date and end_date in user importer
2024-07-23 22:37:19 +01:00
snipe 9baaaae1a3 Merge remote-tracking branch 'origin/develop' 2024-07-23 22:15:57 +01:00
snipe 1f4118a146 Fixed rule
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 22:15:37 +01:00
Marcus Moore f4a3823d88 Handle empty strings for dates by converting them to null 2024-07-23 13:57:28 -07:00
snipe b2a69efc9d Merge remote-tracking branch 'origin/develop' 2024-07-23 21:42:12 +01:00
snipe 00ebc8b64d Merge pull request #15136 from snipe/fixes/cookie_serialization
Remove cookie serialization
2024-07-23 21:41:44 +01:00
snipe effd273245 Merge pull request #14458 from spencerrlongg/bug/sc-24884
Add Form Request and Tests for Update Asset API Method
2024-07-23 20:57:47 +01:00
snipe 7858c72b98 Fixed string
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 20:45:12 +01:00
snipe 09e2c0beab Merge pull request #15147 from uberbrady/fix_setup_ssl_check
Fix setup ssl check
2024-07-23 20:32:13 +01:00
spencerrlongg e8864ffb01 test added, permission fixed 2024-07-23 14:29:17 -05:00
Brady Wetherington d893de2b7e Merge branch 'develop' into fix_setup_ssl_check 2024-07-23 20:24:38 +01:00
Brady Wetherington 7f7cfef81b Mark test as Incomplete, and downgrade error back to debug for tests 2024-07-23 20:24:05 +01:00
Godfrey M d01972bbe5 adds tests for amount and percent 2024-07-23 11:53:59 -07:00
spencerrlongg b9fdb5880a quick push 2024-07-23 13:48:43 -05:00
spencerrlongg 1139ed676a $id to $asset->id (thanks tests) 2024-07-23 13:03:25 -05:00
Spencer Long 64be353156 Merge branch 'develop' into bug/sc-24884 2024-07-23 12:55:19 -05:00
Godfrey M 750015684d purges user storage files 2024-07-23 10:42:50 -07:00
snipe 992d16664d Merge remote-tracking branch 'origin/develop' 2024-07-23 18:26:54 +01:00
snipe 900c19b76d Merge pull request #15143 from snipe/fixes/no-NO-language
Switch to nb-NO from no-NO for Norwegian
2024-07-23 18:25:27 +01:00
snipe effd2bce24 Fixed test to use nb-NO
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 18:22:13 +01:00
snipe de8816d837 Merge pull request #15144 from snipe/localization/updated_strings_2024_07_23
Updated translations
2024-07-23 18:17:45 +01:00
snipe 593554daed Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 18:14:19 +01:00
snipe fecd877e8b Migration
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 17:35:06 +01:00
snipe ba6d8ae8c7 Switch to nb-NO from no-NO
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 17:15:44 +01:00
snipe 175862d3c9 Added route param to message
Signed-off-by: snipe <snipe@snipe.net>
2024-07-23 16:37:01 +01:00
Brady Wetherington 5eea08088d Improve .env file checking to not validate SSL certificates 2024-07-23 16:03:02 +01:00
snipe c752c2a125 Make the passport cookie name configurable
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 14:22:19 +01:00
snipe 4ac4f9b0a9 Customize cookie name
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 14:17:16 +01:00
snipe 09abcb44bb Remove cookie serialization
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 13:49:47 +01:00
snipe 7f566b9152 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/all.css
#	public/css/dist/bootstrap-table.css
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2024-07-22 10:56:37 +01:00
snipe 30835fe9ba Merge pull request #15134 from snipe/upgrade_bs_table
[Snyk] Upgrade bootstrap-table from 1.22.5 to 1.23.0 #15131
2024-07-22 10:50:53 +01:00
snipe 910e98b573 [Snyk] Upgrade bootstrap-table from 1.22.5 to 1.23.0 #15131
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 10:45:35 +01:00
snipe 4623e40077 Merge pull request #15133 from snipe/fixes/load_english_separately_for_table_locale
Load the english file again in case BS table doesn’t have a translation
2024-07-22 10:40:01 +01:00
snipe 20868b9ede Load the english file again in case BS table doesn’t have a translation
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 10:30:42 +01:00
snipe efc84efabf Merge pull request #15132 from snipe/fixes/disable_remote_login_force_via_env
Hides the “Disable Other Login Mechanisms” option via env
2024-07-22 10:20:34 +01:00
snipe 7c7fa96334 Pulled from the app.php as well
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 10:19:55 +01:00
snipe e08acb851c Removed env part, just check for the remote user login
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 10:18:33 +01:00
snipe bbad84c6cc Hides the “Disable Other Login Mechanisms” option via env
Signed-off-by: snipe <snipe@snipe.net>
2024-07-22 10:13:59 +01:00
snipe f2acb98afa Merge pull request #15128 from snipe/fixes/nicer_consumables_layout
Nicer consumables layout
2024-07-20 17:01:15 +01:00
snipe 38affea3f3 More updates
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 16:57:10 +01:00
snipe cea31c5b11 Added more generic buttons with variables
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 16:53:01 +01:00
snipe 254e2f120b Added clone to consumables
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 16:40:32 +01:00
snipe 2bfee0c29a Few more tweaks
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 16:21:11 +01:00
snipe c08bd8f88b Nicer consumables layout
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 16:11:45 +01:00
snipe 9818c2f033 Merge remote-tracking branch 'origin/develop' 2024-07-20 08:08:51 +01:00
snipe f63fd25ce0 Fixed default
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 08:08:29 +01:00
snipe 57ecdd6765 Merge remote-tracking branch 'origin/develop' 2024-07-20 07:43:37 +01:00
snipe 9d890b35c6 Merge pull request #15127 from snipe/fixes/small_default_avatar_tweaks
Fixes #15076 - Removes ability to remove the default avatar from disk
2024-07-20 07:42:39 +01:00
snipe 8fc5c0b5be Use other default avatar, not in the avatars directory
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 07:34:40 +01:00
snipe c4e7448d31 Updated strings
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 07:25:56 +01:00
snipe ffd2687734 Updated language
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 07:25:46 +01:00
snipe 6f3eedf5e6 Removed restoreDefaultAvatar method
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 07:25:27 +01:00
snipe 241eb7b031 One more bad sig
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 06:06:27 +01:00
snipe ca399794c3 Fixed incorrect signature
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 06:05:01 +01:00
snipe 736d4cc59a Use new deleting method in ImageUploadRequest
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 05:55:34 +01:00
snipe ad85f8be2f Cleaned up
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 05:55:12 +01:00
snipe be17ef4047 Added option to restore from default demo
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 05:55:06 +01:00
snipe c6c006f143 Added restore from demo method
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 05:54:40 +01:00
snipe 10856516ac Split out the image deletion into a separate method
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 03:40:16 +01:00
snipe beac4c8b8a Updated tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 03:39:54 +01:00
snipe b39b39bc8a Ignore the test uploads directory
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 03:39:05 +01:00
snipe e373be7dde Skip deleting default avatar from disk on delete
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 01:01:27 +01:00
snipe ca7c0aa47c Added default avatar to seeder
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 00:59:25 +01:00
snipe 48a8cf6b70 Removed unused default avatar
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 00:58:57 +01:00
snipe 7ba1646703 Remove unusued default avatar
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 00:58:42 +01:00
snipe daa550cc82 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2024-07-20 00:31:08 +01:00
snipe e9f9d3c259 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 00:30:03 +01:00
snipe 63377853b8 Merge pull request #15125 from snipe/fixes/display_avatar_on_user_edit
Show existing images on user edit page
2024-07-20 00:29:28 +01:00
snipe fc86eefeac Show existing images on user edit page
Signed-off-by: snipe <snipe@snipe.net>
2024-07-20 00:27:28 +01:00
snipe a98ad76c6a Merge remote-tracking branch 'origin/develop' 2024-07-19 22:55:09 +01:00
snipe 32c768792a Merge pull request #15124 from snipe/fixes/path_for_crypt_in_importer
Fixed use statement for Crypt in the importer, removed unused statements
2024-07-19 22:54:18 +01:00
snipe 793cf27318 Fixed use statement
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 22:50:24 +01:00
snipe ba568b7975 Merge pull request #15123 from snipe/tests/added_bulk_delete_assets
Added tests for bulk asset deletion and restore
2024-07-19 22:36:09 +01:00
snipe 4b5bd76225 Fixed notification use statement
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 22:27:03 +01:00
snipe a54403ef01 Added bulk asset delete test
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 22:26:52 +01:00
snipe 13262c5125 Merge remote-tracking branch 'origin/develop' 2024-07-19 20:05:47 +01:00
snipe 9ea38fbb3a Merge pull request #15122 from snipe/fixes/15121_bulk_delete_and_restore
Fixed #15121 - bulk delete restore logging
2024-07-19 20:03:32 +01:00
snipe 94619e3284 Fixed #15121 - bulk delete restore logging
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 19:56:58 +01:00
snipe 8b05ef6db4 Updated url checks
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 19:48:03 +01:00
snipe 8e03083b83 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2024-07-19 19:07:53 +01:00
snipe dc2debcd83 Add @DrekiDegga as a contributor 2024-07-19 19:07:41 +01:00
snipe b7ab8b9e8f Merge pull request #14530 from DrekiDegga/develop
Added #14426: Makes all Manufacturer links dynamic, not just warranty lookup
2024-07-19 19:07:25 +01:00
snipe c50ab1af67 Merge branch 'develop' into develop 2024-07-19 19:00:56 +01:00
snipe 7773d334ba Merge pull request #15120 from snipe/updated_localizations
Updated strings
2024-07-19 17:54:30 +01:00
snipe 2a0697022e Updated strings
Signed-off-by: snipe <snipe@snipe.net>
2024-07-19 17:47:22 +01:00
snipe e1eb963962 Updated prod assets
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 19:11:51 +01:00
snipe 87c2dc5bb5 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/js/build/app.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2024-07-18 19:11:41 +01:00
snipe 06efcd3c46 Merge branch 'develop' of https://github.com/snipe/snipe-it into develop 2024-07-18 18:55:18 +01:00
snipe faa791e94c Merge pull request #15101 from snipe/localizations/more_strings
More localizations
2024-07-18 18:51:35 +01:00
snipe e328dec9f9 Merge branch 'develop' into localizations/more_strings 2024-07-18 18:51:17 +01:00
snipe 8e60a7b22b Merge pull request #15110 from marcusmoore/fixes/test-namespace-fixes
Fixed a coupled test namespaces
2024-07-18 18:50:27 +01:00
snipe 958b6035e1 Add @r-xyz as a contributor 2024-07-18 18:49:59 +01:00
snipe 94e0739a74 Merge pull request #15115 from r-xyz/alpine-upload-limit
Improved Docker environment settings: implement `PHP_UPLOAD_LIMIT` for Alpine images
2024-07-18 18:49:00 +01:00
snipe 4465aef991 Merge pull request #15114 from snipe/checkout_multiple_accessories
Checkout multiple of an accessory in one checkout
2024-07-18 17:38:19 +01:00
snipe 822bc6f085 Removed response in form request
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 17:37:45 +01:00
snipe 19bd99d159 Updated count
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 17:33:24 +01:00
r-xyz 5c17fefb08 Implement PHP_UPLOAD_LIMIT in Alpine images. 2024-07-18 11:54:58 +02:00
snipe f984b45de2 Merge branch 'develop' into checkout_multiple_accessories 2024-07-18 05:11:51 +01:00
snipe d56252c6b3 Added more back :(
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 05:10:45 +01:00
snipe 44b950cb8e Added back in missing validation
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 05:07:50 +01:00
snipe 985714d504 Test passing now - I hope
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 05:04:17 +01:00
snipe 97ead7120e Use from routes for posting
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 04:54:07 +01:00
snipe 670021a482 Apply the optimize fix
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 04:43:46 +01:00
snipe 9858cc5baf Removed debugging
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 04:43:30 +01:00
snipe fa5b59cf21 Added qty
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 04:29:49 +01:00
snipe 0ef58a9aef Added translation
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 03:48:42 +01:00
snipe 0c4e498df3 Removed debugging
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 03:48:35 +01:00
snipe b5b60f22d5 Removed int
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 03:48:21 +01:00
snipe 2f0c74aef0 Updated tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 03:48:06 +01:00
snipe f56006fb6b More refactoring
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 01:46:53 +01:00
snipe 79a13e3618 Added numCheckedOut method
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:26:58 +01:00
snipe d9b7df5b85 Added form requests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:26:42 +01:00
snipe 0c933bcc5d Cleaned up controllers, use form requests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:25:42 +01:00
snipe 4c4b0f722a Added qty to email notification
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:25:29 +01:00
snipe 6c3cafa72f Added tests
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:25:07 +01:00
snipe 16ae23dbeb Updated validation strings
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:25:00 +01:00
snipe 4f3064bdb1 Added store accesstory form request
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:24:51 +01:00
snipe 2b0627c1f6 Added accessory checkout request
Signed-off-by: snipe <snipe@snipe.net>
2024-07-18 00:24:28 +01:00
snipe 345a4306e8 Added SubstituteBindings
Signed-off-by: snipe <snipe@snipe.net>
2024-07-17 23:02:10 +01:00
snipe beb0836d69 Updated route
Signed-off-by: snipe <snipe@snipe.net>
2024-07-17 23:01:44 +01:00
snipe 05e278d08b Added qty to email
Signed-off-by: snipe <snipe@snipe.net>
2024-07-17 20:58:21 +01:00
snipe c21821a864 Added new form fields
Signed-off-by: snipe <snipe@snipe.net>
2024-07-17 20:58:12 +01:00
Marcus Moore ba13b9924b Fix test namespaces 2024-07-17 11:04:35 -07:00
snipe 2f8306cba8 Merge remote-tracking branch 'origin/develop' 2024-07-17 16:27:16 +01:00
Godfrey M baa7e7d561 clean up 2024-07-16 20:27:38 -07:00
Godfrey M ffaacc04ef cleaned up calculateDepreciation method 2024-07-16 20:24:18 -07:00
snipe d5e1dc54c6 Use translated strings for action log meta
This is only partially complete and I mostly hate it

Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:35:45 +01:00
snipe a3b2912e89 Updated string
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:16:23 +01:00
snipe 7f412ec3f5 Missing string
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:14:41 +01:00
snipe 94e881e5f0 Cleared up language
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:11:03 +01:00
Godfrey M 5bb47e290f validates percentage on store and new depreciations 2024-07-16 13:07:12 -07:00
snipe d53a032402 More consistent UI for indented checkboxes
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:07:03 +01:00
snipe 70d95877d8 Translations for license checkout errors
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 21:02:58 +01:00
Godfrey M 48821f8391 updates transformer, api controller 2024-07-16 12:58:23 -07:00
snipe bb00edda4e Added basic jquery validation translations
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 20:54:48 +01:00
Godfrey M aaa2858337 battling with handling depreciation percentage and amount 2024-07-16 12:25:19 -07:00
snipe 4895023f3b Translate import button
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 19:56:02 +01:00
snipe 0ed637f0ce Translate importer header
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 19:41:47 +01:00
snipe 45bf428092 Translated “seat” for license seat view
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 19:04:51 +01:00
snipe e6f2c457a8 Handle localization better, clearer indenting on addresses
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 19:01:50 +01:00
snipe 04f723327e Fixed translation
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 19:01:23 +01:00
snipe 4a9bd95ed3 Added language support for select2
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 18:40:10 +01:00
snipe 78bd950daa Added select2 language files
Signed-off-by: snipe <snipe@snipe.net>
2024-07-16 18:39:42 +01:00
snipe d3070a0be1 Merge remote-tracking branch 'origin/develop' 2024-07-16 17:24:16 +01:00
snipe 0a70bcc0a9 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	.all-contributorsrc
#	CONTRIBUTORS.md
2024-07-16 16:12:26 +01:00
snipe 39810f9ba5 Merge remote-tracking branch 'origin/develop' 2024-07-12 11:05:10 +01:00
snipe 7464c3622e Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2024-07-11 19:07:54 +01:00
snipe 1a541ce220 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	.all-contributorsrc
#	CONTRIBUTORS.md
2024-07-11 18:38:57 +01:00
snipe c1937b6ea8 Merge remote-tracking branch 'origin/develop' 2024-07-11 12:01:28 +01:00
snipe 7584c30722 Merge remote-tracking branch 'origin/develop' 2024-07-11 12:00:25 +01:00
snipe 62f4b75156 Merge remote-tracking branch 'origin/develop' 2024-07-11 10:08:25 +01:00
snipe 3d5b1b6c6d Merge remote-tracking branch 'origin/develop' 2024-07-10 17:09:56 +01:00
snipe 5f4ee39f2b Merge remote-tracking branch 'origin/develop' 2024-07-10 16:13:00 +01:00
snipe 45d7e2dc42 Merge remote-tracking branch 'origin/develop' 2024-07-10 08:56:29 +01:00
snipe 18f7ca860d Merge remote-tracking branch 'origin/develop' 2024-07-09 19:52:20 +01:00
snipe ea881ff95e Merge remote-tracking branch 'origin/develop' 2024-07-09 13:42:03 +01:00
snipe c1916d6fe4 Merge remote-tracking branch 'origin/develop' 2024-07-09 13:39:07 +01:00
snipe da9932d147 Merge remote-tracking branch 'origin/develop' 2024-07-09 13:36:06 +01:00
snipe da42127a7c Merge remote-tracking branch 'origin/develop' 2024-07-09 11:54:17 +01:00
snipe 090410edb5 Merge remote-tracking branch 'origin/develop' 2024-07-09 11:48:30 +01:00
snipe 43d4bc8dc4 Merge remote-tracking branch 'origin/develop' 2024-07-08 22:13:19 +01:00
snipe ebcc48ec80 Merge remote-tracking branch 'origin/develop' 2024-07-08 22:02:25 +01:00
snipe 5ca62f3ef8 Merge remote-tracking branch 'origin/develop' 2024-07-08 21:39:31 +01:00
snipe 3ae16ff8ca Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	public/js/build/vendor.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2024-07-08 15:53:42 +01:00
snipe 2e2f59b290 Updated production assets
Signed-off-by: snipe <snipe@snipe.net>
2024-07-08 15:29:36 +01:00
snipe c16db436d3 Merge remote-tracking branch 'origin/develop' 2024-07-08 13:21:38 +01:00
snipe 8eb68d3901 Merge remote-tracking branch 'origin/develop' 2024-07-04 17:10:43 +01:00
snipe fc7809192d Merge remote-tracking branch 'origin/develop' 2024-07-03 23:20:24 +01:00
spencerrlongg e79111fed5 fix test :( 2024-07-02 16:01:18 -05:00
spencerrlongg 423b48279b merge 2024-07-02 15:53:28 -05:00
spencerrlongg 6f29c0a7cf Merge branch 'refs/heads/develop' into bug/sc-24884
# Conflicts:
#	routes/api.php
2024-07-02 15:48:39 -05:00
snipe 27bc16604b Add @Galaxy102 as a contributor 2024-07-02 09:48:32 +01:00
snipe 86ae4a3513 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2024-07-02 09:37:40 +01:00
spencerrlongg 0b60cbc531 remove $rules2 2024-07-01 19:37:49 -05:00
snipe e502968707 Prod assets
Signed-off-by: snipe <snipe@snipe.net>
2024-07-01 10:00:28 +01:00
snipe 12dcac4994 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/all.css
#	public/css/dist/bootstrap-table.css
#	public/js/build/app.js
#	public/js/dist/all.js
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2024-07-01 10:00:15 +01:00
snipe 1837da4508 Merge branch 'develop' 2024-06-26 13:46:40 +01:00
snipe e1a6b441d7 Merge remote-tracking branch 'origin/develop' 2024-06-26 12:53:18 +01:00
snipe 23c1d664fe Prod assets
Signed-off-by: snipe <snipe@snipe.net>
2024-06-24 22:07:01 +01:00
snipe c891342fdb Bumoed version
Signed-off-by: snipe <snipe@snipe.net>
2024-06-24 22:06:52 +01:00
spencerrlongg 8ce577db37 adds @snipe's rules for undeleted assigned targets 2024-05-23 15:51:26 -05:00
Spencer Long cdb1140f10 Merge branch 'develop' into bug/sc-24884 2024-05-23 13:53:00 -05:00
spencerrlongg 97a6152ea9 update comment per @uberbrady 2024-04-25 17:47:55 -05:00
spencerrlongg 03f091a77f ammended note, got real rule in there 2024-04-25 17:17:42 -05:00
spencerrlongg 8696a423b0 another option 2024-04-23 17:38:40 -05:00
spencerrlongg 107f8db9bc another option 2024-04-23 17:35:33 -05:00
spencerrlongg b11c900a4c fix bracket, + overwrite required rulesets 2024-04-23 16:29:07 -05:00
Spencer Long 53ccd196d7 Merge branch 'develop' into bug/sc-24884 2024-04-23 13:57:59 -05:00
spencerrlongg 99d7155729 translation strings 2024-04-07 19:50:53 -05:00
spencerrlongg f30439a544 small refactor, pretty much good to go now though 2024-04-07 17:07:46 -05:00
spencerrlongg 997eddfed1 cleanup + notes for monday 2024-04-04 18:23:03 -05:00
spencerrlongg 52340aca78 just about wrapped up 2024-04-04 17:41:10 -05:00
spencerrlongg e1fb446888 this is a pretty good start, need to know about other PR 2024-04-04 14:20:03 -05:00
steve@degga.net 83443ad2b5 Sometimes I should just slow down. 2024-04-04 11:36:21 -04:00
steve@degga.net 3fcc067481 Removes accidental changes on two lines 2024-04-04 10:27:44 -04:00
steve@degga.net 3e564eaf19 fixed accidentally forgot update a call to the old dynamicWarrantyUrl() method in hardware/view.blade.php 2024-04-03 20:09:37 -04:00
spencerrlongg 0a90df2b14 alright conflicts resolved 2024-04-03 14:40:14 -05:00
Spencer Long 4ab75c1c03 Merge branch 'develop' into bug/sc-24884 2024-04-03 14:08:41 -05:00
steve@degga.net bf10fd0cf0 Rename dynamicUrl() method 2024-03-31 22:37:41 -04:00
steve@degga.net 83ce04dc8d Fixes syntax error. 2024-03-31 22:31:06 -04:00
steve@degga.net f7bbec6be4 Makes all manufacturer URLs dynamic, not just warranty lookup. 2024-03-31 22:18:44 -04:00
spencerrlongg cec84b857b fixed last audit date + test 2024-03-27 14:39:23 -05:00
spencerrlongg 1d4a7a7b02 added audit dates 2024-03-27 14:05:30 -05:00
spencerrlongg 701411c1b9 get rid of a couple unnecessary changes 2024-03-27 13:53:57 -05:00
Spencer Long 013463aafc Merge branch 'develop' into bug/sc-24884 2024-03-27 12:45:45 -05:00
spencerrlongg 39c15b2868 reformat array 2024-03-27 10:35:25 -05:00
spencerrlongg 60ca634eff remove interactswithsettings 2024-03-21 12:04:53 -05:00
spencerrlongg be282dd038 resolve a couple issues 2024-03-21 09:26:45 -05:00
spencerrlongg 8cc1397ace rm a couple unnecessary 2024-03-20 15:36:53 -05:00
spencerrlongg e7b9903341 delete some extra lines 2024-03-20 15:29:06 -05:00
spencerrlongg e3e01e07b1 final cleanup 2024-03-20 15:23:45 -05:00
spencerrlongg d18aa1db98 some more cleanup + tests 2024-03-20 15:18:15 -05:00
spencerrlongg c155e4a7c9 new test for not found assets 2024-03-20 13:52:22 -05:00
spencerrlongg fdf0be09db all tests passing 2024-03-20 13:43:01 -05:00
spencerrlongg e1addc5aef oops, typo from conflict resolve 2024-03-20 13:16:36 -05:00
Spencer Long b4b4927370 Merge branch 'develop' into bug/sc-24884 2024-03-20 12:36:39 -05:00
spencerrlongg 0ffceb9691 some notes and a little progress 2024-03-20 00:07:52 -05:00
spencerrlongg 1e810d2426 most tests now passing, still one broken 2024-03-19 19:47:26 -05:00
spencerrlongg c0110e7f29 some more tests and refinement 2024-03-19 19:27:35 -05:00
spencerrlongg 86ab880c90 buncha progress 2024-03-19 15:34:59 -05:00
spencerrlongg f6ab0f8f46 lots of cleanup to do, but this DOES work 2024-03-13 15:57:10 -05:00
spencerrlongg f01b205486 some changes 2024-03-13 15:10:51 -05:00
spencerrlongg 8962ced038 push to switch branches 2024-03-13 10:40:50 -05:00
spencerrlongg 04d7884af8 some more testing stuff 2024-03-12 23:17:40 -05:00
spencerrlongg 6732b6601e some cool progress, but something with unique not working 2024-03-12 18:33:59 -05:00
spencerrlongg eb8f1dd553 some cleanup 2024-03-09 12:29:26 -06:00
spencerrlongg c8341d9dc4 aha, got it working. 2024-03-08 19:48:47 -06:00
spencerrlongg b239b3a4db some good progress, lots of testing needs to be done on the new inclusion of SubstituteBindings 2024-03-08 18:24:41 -06:00
spencerrlongg eac01868ca not all working, but pushing to work on something else 2024-03-05 11:02:55 -06:00
spencerrlongg c025e25839 just the basics and notes, pushing to keep track 2024-02-26 14:32:50 -06:00
1636 changed files with 29740 additions and 268399 deletions
+27
View File
@@ -3154,6 +3154,33 @@
"contributions": [
"code"
]
},
{
"login": "r-xyz",
"name": "r-xyz",
"avatar_url": "https://avatars.githubusercontent.com/u/100710244?v=4",
"profile": "https://github.com/r-xyz",
"contributions": [
"code"
]
},
{
"login": "DrekiDegga",
"name": "Steven Mainor",
"avatar_url": "https://avatars.githubusercontent.com/u/47491036?v=4",
"profile": "https://github.com/DrekiDegga",
"contributions": [
"code"
]
},
{
"login": "arne-kroeger",
"name": "arne-kroeger",
"avatar_url": "https://avatars.githubusercontent.com/u/65785975?v=4",
"profile": "https://github.com/arne-kroeger",
"contributions": [
"code"
]
}
]
}
+1 -1
View File
@@ -14,7 +14,7 @@ APP_KEY=base64:3ilviXqB9u6DX1NRcyWGJ+sjySF+H18CPDGb3+IVwMQ=
APP_URL=http://localhost:8000
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - TZ identifier
APP_TIMEZONE='UTC'
APP_LOCALE=en
APP_LOCALE=en-US
MAX_RESULTS=500
# --------------------------------------------
+1 -1
View File
@@ -6,7 +6,7 @@ APP_DEBUG=false
APP_KEY=base64:hTUIUh9CP6dQx+6EjSlfWTgbaMaaRvlpEwk45vp+xmk=
APP_URL=http://127.0.0.1:8000
APP_TIMEZONE='US/Eastern'
APP_LOCALE=en
APP_LOCALE=en-US
APP_LOCKED=false
MAX_RESULTS=200
+1
View File
@@ -87,6 +87,7 @@ SESSION_LIFETIME=12000
EXPIRE_ON_CLOSE=false
ENCRYPT=false
COOKIE_NAME=snipeit_session
PASSPORT_COOKIE_NAME='snipeit_passport_token'
COOKIE_DOMAIN=null
SECURE_COOKIES=false
API_TOKEN_EXPIRATION_YEARS=15
+3
View File
@@ -67,3 +67,6 @@ _ide_helper_models.php
/.phplint-cache
storage/ldap_client_tls.cert
storage/ldap_client_tls.key
/storage/framework/testing
/.phpunit.cache
+1 -1
View File
@@ -51,7 +51,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<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/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") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
+30 -30
View File
@@ -1,35 +1,35 @@
FROM alpine:3.18.6
FROM alpine:3.19
# Apache + PHP
RUN apk add --no-cache \
apache2 \
php81 \
php81-common \
php81-apache2 \
php81-curl \
php81-ldap \
php81-mysqli \
php81-gd \
php81-xml \
php81-mbstring \
php81-zip \
php81-ctype \
php81-tokenizer \
php81-pdo_mysql \
php81-openssl \
php81-bcmath \
php81-phar \
php81-json \
php81-iconv \
php81-fileinfo \
php81-simplexml \
php81-session \
php81-dom \
php81-xmlwriter \
php81-xmlreader \
php81-sodium \
php81-redis \
php81-pecl-memcached \
php81-exif \
php82 \
php82-common \
php82-apache2 \
php82-curl \
php82-ldap \
php82-mysqli \
php82-gd \
php82-xml \
php82-mbstring \
php82-zip \
php82-ctype \
php82-tokenizer \
php82-pdo_mysql \
php82-openssl \
php82-bcmath \
php82-phar \
php82-json \
php82-iconv \
php82-fileinfo \
php82-simplexml \
php82-session \
php82-dom \
php82-xmlwriter \
php82-xmlreader \
php82-sodium \
php82-redis \
php82-pecl-memcached \
php82-exif \
curl \
wget \
vim \
@@ -42,7 +42,7 @@ COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
# Where apache's PID lives
RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php81/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php82/php.ini
COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf
# Enable mod_rewrite
+1 -1
View File
@@ -20,7 +20,7 @@ APP_DEBUG=true
APP_KEY=base64:glJpcM7BYwWiBggp3SQ/+NlRkqsBQMaGEOjemXqJzOU=
APP_URL=http://localhost:8000
APP_TIMEZONE='UTC'
APP_LOCALE=en
APP_LOCALE=en-US
# --------------------------------------------
# REQUIRED: DATABASE SETTINGS
@@ -0,0 +1,66 @@
<?php
namespace App\Console\Commands;
use App\Models\Asset;
use Illuminate\Console\Command;
class FixupAssignedToWithoutAssignedType extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:assigned-to-fixup
{--debug : Display debugging output}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes up assets that have an assigned_to but no assigned_type';
/**
* Execute the console command.
*/
public function handle()
{
$assets = Asset::whereNull("assigned_type")->whereNotNull("assigned_to")->withTrashed();
$this->withProgressBar($assets->get(), function (Asset $asset) {
//now check each action log, from the most recent backwards, to find the last checkin or checkout
foreach($asset->log()->orderBy("id","desc")->get() as $action_log) {
if($this->option("debug")) {
$this->info("Asset id: " . $asset->id . " action log, action type is: " . $action_log->action_type);
}
switch($action_log->action_type) {
case 'checkin from':
if($this->option("debug")) {
$this->info("Doing a checkin for ".$asset->id);
}
$asset->assigned_to = null;
// if you have a required custom field, we still want to save, and we *don't* want an action_log
$asset->saveQuietly();
return;
case 'checkout':
if($this->option("debug")) {
$this->info("Doing a checkout for " . $asset->id . " picking target type: " . $action_log->target_type);
}
if($asset->assigned_to != $action_log->target_id) {
$this->error("Asset's assigned_to does *NOT* match Action Log's target_id. \$asset->assigned_to=".$asset->assigned_to." vs. \$action_log->target_id=".$action_log->target_id);
//FIXME - do we abort here? Do we try to keep looking? I don't know, this means your data is *really* messed up...
}
$asset->assigned_type = $action_log->target_type;
$asset->saveQuietly(); // see above
return;
}
}
$asset->assigned_to = null; //asset was never checked in or out in its lifetime - it stays 'checked in'
$asset->saveQuietly(); //see above
});
$this->newLine();
$this->info("Assets assigned_type are fixed");
}
}
+17
View File
@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
@@ -15,6 +16,8 @@ use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class Purge extends Command
{
@@ -141,6 +144,20 @@ class Purge extends Command
$this->info($users->count().' users purged.');
$user_assoc = 0;
foreach ($users as $user) {
$rel_path = 'private_uploads/users';
$filenames = Actionlog::where('action_type', 'uploaded')
->where('item_id', $user->id)
->pluck('filename');
foreach($filenames as $filename) {
try {
if (Storage::exists($rel_path . '/' . $filename)) {
Storage::delete($rel_path . '/' . $filename);
}
} catch (\Exception $e) {
Log::info('An error occurred while deleting files: ' . $e->getMessage());
}
}
$this->info('- User "'.$user->username.'" deleted.');
$user_assoc += $user->userlog()->count();
$user->userlog()->forceDelete();
@@ -73,6 +73,7 @@ class ResetDemoSettings extends Command
$settings->saml_forcelogin = '0';
$settings->saml_slo = null;
$settings->saml_custom_settings = null;
$settings->default_avatar = 'default.png';
$settings->save();
+26 -6
View File
@@ -30,8 +30,11 @@ class SQLStreamer {
public function parse_sql(string $line): string {
// take into account the 'start of line or not' setting as an instance variable?
// 'continuation' lines for a permitted statement are PERMITTED.
// remove *only* line-feeds & carriage-returns; helpful for regexes against lines from
// Windows dumps
$line = trim($line, "\r\n");
if($this->statement_is_permitted && $line[0] === ' ') {
return $line;
return $line . "\n"; //re-add the newline
}
$table_regex = '`?([a-zA-Z0-9_]+)`?';
@@ -42,8 +45,12 @@ class SQLStreamer {
"/^(INSERT INTO )$table_regex(.*)$/" => false,
"/^UNLOCK TABLES/" => false,
// "/^\\) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/" => false, // FIXME not sure what to do here?
"/^\\)[a-zA-Z0-9_= ]*;$/" => false
// ^^^^^^ that bit should *exit* the 'perimitted' black
"/^\\)[a-zA-Z0-9_= ]*;$/" => false,
// ^^^^^^ that bit should *exit* the 'permitted' block
"/^\\(.*\\)[,;]$/" => false, //older MySQL dump style with one set of values per line
/* we *could* have made the ^INSERT INTO blah VALUES$ turn on the capturing state, and closed it with
a ^(blahblah);$ but it's cleaner to not have to manage the state machine. We're just going to
assume that (blahblah), or (blahblah); are values for INSERT and are always acceptable. */
];
foreach($allowed_statements as $statement => $statechange) {
@@ -67,7 +74,7 @@ class SQLStreamer {
}
//how do we *replace* the tablename?
// print "RETURNING LINE: $line";
return $line;
return $line . "\n"; //re-add newline
}
}
// all that is not allowed is denied.
@@ -85,7 +92,7 @@ class SQLStreamer {
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
//can't use 'users' because the 'accessories_users' table?
//can't use 'users' because the 'accessories_checkout' table?
// can't use 'assets' because 'ver1_components_assets'
foreach($check_tables as $check_table => $_ignore) {
foreach ($parser->tablenames as $tablename => $_count) {
@@ -164,7 +171,8 @@ class RestoreFromBackup extends Command
{filename : The zip file to be migrated}
{--no-progress : Don\'t show a progress bar}
{--sanitize-guess-prefix : Guess and output the table-prefix needed to "sanitize" the SQL}
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}';
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}
{--sql-stdout-only : ONLY "Sanitize" the SQL and print it to stdout - useful for debugging - probably requires --sanitize-with-prefix= }';
/**
* The console command description.
@@ -365,6 +373,15 @@ class RestoreFromBackup extends Command
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
}
// If we're doing --sql-stdout-only, handle that now so we don't have to open pipes to mysql and all of that silliness
if ($this->option('sql-stdout-only')) {
$sql_importer = new SQLStreamer($sql_contents, STDOUT, $this->option('sanitize-with-prefix'));
$bytes_read = $sql_importer->line_aware_piping();
return $this->warn("$bytes_read total bytes read");
//TODO - it'd be nice to dump this message to STDERR so that STDOUT is just pure SQL,
// which would be good for redirecting to a file, and not having to trim the last line off of it
}
//how to invoke the restore?
$pipes = [];
@@ -466,6 +483,9 @@ class RestoreFromBackup extends Command
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (!is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
}
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
+62 -21
View File
@@ -62,8 +62,9 @@ class Helper
'mn' => 'mn-MN', // Mongolian
'ms' => 'ms-MY', // Malay
'nl' => 'nl-NL', // Dutch
'no' => 'no-NO', // Norwegian
'no' => 'nb-NO', // Norwegian Bokmål
'pl' => 'pl-PL', // Polish
'pt' => 'pt-PT', // Portuguese
'ro' => 'ro-RO', // Romanian
'ru' => 'ru-RU', // Russian
'sk' => 'sk-SK', // Slovak
@@ -720,7 +721,7 @@ class Helper
{
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
$components = Component::whereNotNull('min_amt')->get();
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
$licenses = License::where('min_amt', '>', 0)->get();
@@ -748,7 +749,7 @@ class Helper
}
foreach ($accessories as $accessory) {
$avail = $accessory->qty - $accessory->users_count;
$avail = $accessory->qty - $accessory->checkouts_count;
if ($avail < ($accessory->min_amt) + $alert_threshold) {
if ($accessory->qty > 0) {
$percent = number_format((($avail / $accessory->qty) * 100), 0);
@@ -913,13 +914,22 @@ class Helper
$rules = $class::rules();
foreach ($rules as $rule_name => $rule) {
if ($rule_name == $field) {
if (strpos($rule, 'required') === false) {
return false;
if (is_array($rule)) {
if (in_array('required', $rule)) {
return true;
} else {
return false;
}
} else {
return true;
}
if (strpos($rule, 'required') === false) {
return false;
} else {
return true;
}
}
}
}
return false;
}
/**
@@ -1440,7 +1450,6 @@ class Helper
foreach (self::$language_map as $legacy => $new) {
if ($language_code == $legacy) {
Log::debug('Current language is '.$legacy.', using '.$new.' instead');
return $new;
}
}
@@ -1451,6 +1460,7 @@ class Helper
public static function mapBackToLegacyLocale($new_locale = null)
{
if (strlen($new_locale) <= 4) {
return $new_locale; //"new locale" apparently wasn't quite so new
}
@@ -1458,42 +1468,73 @@ class Helper
// This does a *reverse* search against our new language map array - given the value, find the *key* for it
$legacy_locale = array_search($new_locale, self::$language_map);
if($legacy_locale !== false) {
if ($legacy_locale !== false) {
return $legacy_locale;
}
return $new_locale; // better that you have some weird locale that doesn't fit into our mappings anywhere than 'void'
}
public static function determineLanguageDirection() {
return in_array(app()->getLocale(),
[
'ar-SA',
'fa-IR',
'he-IL'
]) ? 'rtl' : 'ltr';
}
static public function getRedirectOption($request, $id, $table, $asset_id = null)
static public function getRedirectOption($request, $id, $table, $item_id = null)
{
$redirect_option = Session::get('redirect_option');
$checkout_to_type = Session::get('checkout_to_type');
//return to index
if ($redirect_option == '0') {
// return to index
if ($redirect_option == 'index') {
switch ($table) {
case "Assets":
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.checkout.success'));
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 to thing being assigned
if ($redirect_option == '1') {
// return to thing being assigned
if ($redirect_option == 'item') {
switch ($table) {
case "Assets":
return redirect()->route('hardware.show', $id ? $id : $asset_id)->with('success', trans('admin/hardware/message.checkout.success'));
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 to thing being assigned to
if ($redirect_option == '2') {
// return to assignment target
if ($redirect_option == 'target') {
switch ($checkout_to_type) {
case 'user':
return redirect()->route('users.show', $request->assigned_user)->with('success', trans('admin/hardware/message.checkout.success'));
return route('users.show', ['user' => $request->assigned_user]);
case 'location':
return redirect()->route('locations.show', $request->assigned_location)->with('success', trans('admin/hardware/message.checkout.success'));
return route('locations.show', ['location' => $request->assigned_location]);
case 'asset':
return redirect()->route('hardware.show', $request->assigned_asset)->with('success', trans('admin/hardware/message.checkout.success'));
return route('hardware.show', ['hardware' => $request->assigned_asset]);
}
}
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
@@ -79,10 +79,11 @@ class AccessoriesController extends Controller
$accessory = $request->handleImages($accessory);
session()->put(['redirect_option' => $request->get('redirect_option')]);
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -143,12 +144,12 @@ class AccessoriesController extends Controller
*/
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
{
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
$this->authorize($accessory);
$validator = Validator::make($request->all(), [
"qty" => "required|numeric|min:$accessory->users_count"
"qty" => "required|numeric|min:$accessory->checkouts_count"
]);
if ($validator->fails()) {
@@ -176,9 +177,10 @@ class AccessoriesController extends Controller
$accessory = $request->handleImages($accessory);
// Was the accessory updated?
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.update.success'));
return redirect()->to(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'));
@@ -231,7 +233,7 @@ class AccessoriesController extends Controller
*/
public function show($accessoryID = null) : View | RedirectResponse
{
$accessory = Accessory::withCount('users as users_count')->find($accessoryID);
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
$this->authorize('view', $accessory);
if (isset($accessory->id)) {
return view('accessories/view', compact('accessory'));
@@ -3,8 +3,10 @@
namespace App\Http\Controllers\Accessories;
use App\Events\CheckoutableCheckedIn;
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;
@@ -23,7 +25,7 @@ class AccessoryCheckinController extends Controller
*/
public function create($accessoryUserId = null, $backto = null) : View | RedirectResponse
{
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
if (is_null($accessory_user = DB::table('accessories_checkout')->find($accessoryUserId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
}
@@ -38,16 +40,16 @@ class AccessoryCheckinController extends Controller
*
* @uses Accessory::checkin_email() to determine if an email can and should be sent
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param null $accessoryUserId
* @param null $accessoryCheckoutId
* @param string $backto
*/
public function store(Request $request, $accessoryUserId = null, $backto = null) : RedirectResponse
public function store(Request $request, $accessoryCheckoutId = null, $backto = null) : RedirectResponse
{
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryCheckoutId))) {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
$accessory = Accessory::find($accessory_user->accessory_id);
$accessory = Accessory::find($accessory_checkout->accessory_id);
$this->authorize('checkin', $accessory);
@@ -58,12 +60,12 @@ class AccessoryCheckinController extends Controller
}
// Was the accessory updated?
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
$return_to = e($accessory_user->assigned_to);
if ($accessory_checkout->delete()) {
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
event(new CheckoutableCheckedIn($accessory, User::find($return_to), auth()->user(), $request->input('note'), $checkin_at));
session()->put(['redirect_option' => $request->get('redirect_option')]);
return redirect()->route('accessories.show', $accessory->id)->with('success', trans('admin/accessories/message.checkin.success'));
return redirect()->to(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'));
@@ -3,18 +3,24 @@
namespace App\Http\Controllers\Accessories;
use App\Events\CheckoutableCheckedOut;
use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
use App\Http\Requests\AccessoryCheckoutRequest;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
class AccessoryCheckoutController extends Controller
{
use CheckInOutRequest;
/**
* Return the form to checkout an Accessory to a user.
*
@@ -24,7 +30,7 @@ class AccessoryCheckoutController extends Controller
public function create($id) : View | RedirectResponse
{
if ($accessory = Accessory::withCount('users as users_count')->find($id)) {
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($id)) {
$this->authorize('checkout', $accessory);
@@ -57,44 +63,38 @@ class AccessoryCheckoutController extends Controller
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param Request $request
* @param int $accessoryId
* @param Accessory $accessory
*/
public function store(Request $request, $accessoryId) : RedirectResponse
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
{
// Check if the accessory exists
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.user_not_found'));
}
$this->authorize('checkout', $accessory);
if (!$user = User::find($request->input('assigned_to'))) {
return redirect()->route('accessories.checkout.show', $accessory->id)->with('error', trans('admin/accessories/message.checkout.user_does_not_exist'));
$target = $this->determineCheckoutTarget();
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
}
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
// Make sure there is at least one available to checkout
if ($accessory->numRemaining() <= 0){
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkout.unavailable'));
}
// Set this as user since we only allow checkout to user for this item type
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
$request->request->add(['assigned_user' => $target->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
// Update the accessory data
$accessory->assigned_to = e($request->input('assigned_to'));
$accessory->users()->attach($accessory->id, [
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to'),
'note' => $request->input('note'),
]);
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
// Redirect to the new accessory page
return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success'));
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))
->with('success', trans('admin/accessories/message.checkout.success'));
}
}
@@ -218,6 +218,7 @@ class AcceptanceController extends Controller
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'eula' => $item->getEula(),
'note' => $request->input('note'),
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
@@ -308,6 +309,7 @@ class AcceptanceController extends Controller
'item_tag' => $item->asset_tag,
'item_model' => $display_model,
'item_serial' => $item->serial,
'item_status' => $item->assetstatus?->name,
'note' => $request->input('note'),
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
@@ -4,7 +4,10 @@ namespace App\Http\Controllers\Api;
use App\Events\CheckoutableCheckedOut;
use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
use App\Http\Requests\AccessoryCheckoutRequest;
use App\Http\Requests\StoreAccessoryRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
@@ -15,10 +18,12 @@ use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use App\Models\AccessoryCheckout;
class AccessoriesController extends Controller
{
use CheckInOutRequest;
/**
* Display a listing of the resource.
*
@@ -46,13 +51,13 @@ class AccessoriesController extends Controller
'min_amt',
'company_id',
'notes',
'users_count',
'checkouts_count',
'qty',
];
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
->withCount('users as users_count');
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
->withCount('checkouts as checkouts_count');
if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search'));
@@ -121,12 +126,12 @@ class AccessoriesController extends Controller
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\ImageUploadRequest $request
* @return \Illuminate\Http\JsonResponse
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @return \Illuminate\Http\Response
*/
public function store(ImageUploadRequest $request)
public function store(StoreAccessoryRequest $request)
{
$this->authorize('create', Accessory::class);
$accessory = new Accessory;
@@ -144,15 +149,15 @@ class AccessoriesController extends Controller
/**
* Display the specified resource.
*
* @param int $id
* @return array
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::withCount('users as users_count')->findOrFail($id);
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
return (new AccessoriesTransformer)->transformAccessory($accessory);
}
@@ -161,10 +166,10 @@ class AccessoriesController extends Controller
/**
* Display the specified resource.
*
* @param int $id
* @return array
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
*/
public function accessory_detail($id)
{
@@ -195,28 +200,23 @@ class AccessoriesController extends Controller
$offset = request('offset', 0);
$limit = request('limit', 50);
$accessory_users = $accessory->users;
$total = $accessory_users->count();
$accessory_checkouts = $accessory->checkouts;
$total = $accessory_checkouts->count();
if ($total < $offset) {
$offset = 0;
}
$accessory_users = $accessory->users()->skip($offset)->take($limit)->get();
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
if ($request->filled('search')) {
$accessory_users = $accessory->users()
->where(function ($query) use ($request) {
$search_str = '%' . $request->input('search') . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
})
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
->get();
$total = $accessory_users->count();
$total = $accessory_checkouts->count();
}
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total);
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
}
@@ -273,43 +273,31 @@ class AccessoriesController extends Controller
* If Slack is enabled and/or asset acceptance is enabled, it will also
* trigger a Slack message and send an email.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $accessoryId
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\JsonResponse
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
public function checkout(Request $request, $accessoryId)
public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory)
{
// Check if the accessory exists
if (is_null($accessory = Accessory::withCount('users as users_count')->find($accessoryId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
}
$this->authorize('checkout', $accessory);
$target = $this->determineCheckoutTarget();
$accessory->checkout_qty = $request->input('checkout_qty', 1);
if ($accessory->numRemaining() > 0) {
if (! $user = User::find($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.checkout.user_does_not_exist')));
}
// Update the accessory data
$accessory->assigned_to = $request->input('assigned_to');
$accessory->users()->attach($accessory->id, [
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => Auth::id(),
'assigned_to' => $request->get('assigned_to'),
'note' => $request->get('note'),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'No accessories remaining'));
// Set this value to be able to pass the qty through to the event
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
}
@@ -326,19 +314,19 @@ class AccessoriesController extends Controller
*/
public function checkin(Request $request, $accessoryUserId = null)
{
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryUserId))) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
}
$accessory = Accessory::find($accessory_user->accessory_id);
$accessory = Accessory::find($accessory_checkout->accessory_id);
$this->authorize('checkin', $accessory);
$logaction = $accessory->logCheckin(User::find($accessory_user->assigned_to), $request->input('note'));
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
// Was the accessory updated?
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
if (! is_null($accessory_user->assigned_to)) {
$user = User::find($accessory_user->assigned_to);
if ($accessory_checkout->delete()) {
if (! is_null($accessory_checkout->assigned_to)) {
$user = User::find($accessory_checkout->assigned_to);
}
$data['log_id'] = $logaction->id;
+32 -38
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
@@ -651,36 +652,35 @@ class AssetsController extends Controller
* Accepts a POST request to update an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param \App\Http\Requests\ImageUploadRequest $request
* @since [v4.0]
*/
public function update(ImageUploadRequest $request, $id) : JsonResponse
public function update(UpdateAssetRequest $request, Asset $asset): JsonResponse
{
$this->authorize('update', Asset::class);
$asset->fill($request->validated());
if ($asset = Asset::find($id)) {
$asset->fill($request->all());
if ($request->has('model_id')) {
$asset->model()->associate(AssetModel::find($request->validated()['model_id']));
}
if ($request->has('company_id')) {
$asset->company_id = Company::getIdForCurrentUser($request->validated()['company_id']);
}
if ($request->has('rtd_location_id') && !$request->has('location_id')) {
$asset->location_id = $request->validated()['rtd_location_id'];
}
if ($request->input('last_audit_date')) {
$asset->last_audit_date = Carbon::parse($request->input('last_audit_date'))->startOfDay()->format('Y-m-d H:i:s');
}
($request->filled('model_id')) ?
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
($request->filled('rtd_location_id')) ?
$asset->location_id = $request->get('rtd_location_id') : '';
($request->filled('company_id')) ?
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
/**
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
if ($request->has('image_source')) {
$request->offsetSet('image', $request->offsetGet('image_source'));
}
($request->filled('rtd_location_id')) ?
$asset->location_id = $request->get('rtd_location_id') : null;
/**
* this is here just legacy reasons. Api\AssetController
* used image_source once to allow encoded image uploads.
*/
if ($request->has('image_source')) {
$request->offsetSet('image', $request->offsetGet('image_source'));
}
$asset = $request->handleImages($asset);
$model = AssetModel::find($asset->model_id);
$asset = $request->handleImages($asset);
$model = $asset->model;
// Update custom fields
$problems_updating_encrypted_custom_fields = false;
@@ -706,15 +706,13 @@ class AssetsController extends Controller
}
}
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id;
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
$location = $target->location_id;
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $id)
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
->update(['location_id' => $target->location_id]);
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
$location = $target->id;
@@ -728,17 +726,13 @@ class AssetsController extends Controller
$asset->image = $asset->getImageUrl();
}
if ($problems_updating_encrypted_custom_fields) {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
} else {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
}
if ($problems_updating_encrypted_custom_fields) {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
} else {
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
@@ -934,7 +928,7 @@ class AssetsController extends Controller
}
}
if ($request->has('status_id')) {
if ($request->filled('status_id')) {
$asset->status_id = $request->input('status_id');
}
@@ -984,7 +978,7 @@ class AssetsController extends Controller
public function checkinByTag(Request $request, $tag = null) : JsonResponse
{
$this->authorize('checkin', Asset::class);
if(null == $tag && null !== ($request->input('asset_tag'))) {
if (null == $tag && null !== ($request->input('asset_tag'))) {
$tag = $request->input('asset_tag');
}
$asset = Asset::where('asset_tag', $tag)->first();
@@ -20,9 +20,9 @@ class DepreciationsController extends Controller
public function index(Request $request) : JsonResponse | array
{
$this->authorize('view', Depreciation::class);
$allowed_columns = ['id','name','months','depreciation_min','created_at'];
$allowed_columns = ['id','name','months','depreciation_min', 'depreciation_type','created_at'];
$depreciations = Depreciation::select('id','name','months','depreciation_min','user_id','created_at','updated_at');
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','user_id','created_at','updated_at');
if ($request->filled('search')) {
$depreciations = $depreciations->TextSearch($request->input('search'));
@@ -24,7 +24,7 @@ class LicensesController extends Controller
{
$this->authorize('view', License::class);
$licenses = License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count');
$licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count');
if ($request->filled('company_id')) {
$licenses->where('company_id', '=', $request->input('company_id'));
@@ -70,6 +70,9 @@ class LicensesController extends Controller
$licenses->where('depreciation_id', '=', $request->input('depreciation_id'));
}
if ($request->filled('user_id')) {
$licenses->where('user_id', '=', $request->input('user_id'));
}
if (($request->filled('maintained')) && ($request->input('maintained')=='true')) {
$licenses->where('maintained','=',1);
@@ -113,6 +116,9 @@ class LicensesController extends Controller
case 'company':
$licenses = $licenses->leftJoin('companies', 'licenses.company_id', '=', 'companies.id')->orderBy('companies.name', $order);
break;
case 'created_by':
$licenses = $licenses->OrderCreatedBy($order);
break;
default:
$allowed_columns =
[
@@ -5,8 +5,10 @@ namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Controllers\Controller;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Asset;
use App\Models\Location;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -222,6 +224,15 @@ class LocationsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
public function assets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
$assets = Asset::where('assigned_to', '=', $location->id)->where('assigned_type', '=', Location::class)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
$assets = $assets->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
/**
* Remove the specified resource from storage.
*
@@ -237,6 +248,7 @@ class LocationsController extends Controller
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count')
->withCount('accessories as accessories_count')
->findOrFail($id);
if (! $location->isDeletable()) {
+10 -2
View File
@@ -83,11 +83,19 @@ class ReportsController extends Controller
$offset = ($request->input('offset') > $total) ? $total : app('api_offset_value');
$limit = app('api_limit_value');
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
switch ($request->input('sort')) {
case 'admin':
$actionlogs->OrderAdmin($order);
break;
default:
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
$actionlogs = $actionlogs->orderBy($sort, $order);
break;
}
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
$actionlogs = $actionlogs->skip($offset)->take($limit)->get();
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
}
@@ -8,6 +8,7 @@ use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\StatuslabelsTransformer;
use App\Models\Asset;
use App\Models\Setting;
use App\Models\Statuslabel;
use Illuminate\Http\Request;
use App\Http\Transformers\PieChartTransformer;
@@ -187,8 +188,14 @@ class StatuslabelsController extends Controller
public function getAssetCountByStatuslabel() : array
{
$this->authorize('view', Statuslabel::class);
$statuslabels = Statuslabel::withCount('assets')->get();
$total = Array();
if (Setting::getSettings()->show_archived_in_list == 0 ) {
$statuslabels = Statuslabel::withCount('assets')->where('archived','0')->get();
} else {
$statuslabels = Statuslabel::withCount('assets')->get();
}
$total = [];
foreach ($statuslabels as $statuslabel) {
@@ -11,7 +11,6 @@ use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Log;
use \Illuminate\Contracts\View\View;
use \Illuminate\Http\RedirectResponse;
@@ -83,7 +82,6 @@ class AssetCheckinController extends Controller
}
$asset->expected_checkin = null;
//$asset->last_checkout = null;
$asset->last_checkin = now();
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
@@ -128,12 +126,12 @@ class AssetCheckinController extends Controller
$acceptance->delete();
});
Session::put('redirect_option', $request->get('redirect_option'));
// Was the asset updated?
session()->put('redirect_option', $request->get('redirect_option'));
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
return Helper::getRedirectOption($asset, $assetId, 'Assets');
return redirect()->to(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());
@@ -109,10 +109,11 @@ class AssetCheckoutController 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')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return Helper::getRedirectOption($request, $assetId, 'Assets');
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('admin/hardware/message.checkout.error').$asset->getErrors());
@@ -204,9 +204,13 @@ class AssetsController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($success) {
return redirect()->route('hardware.index')
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset->id), 'id', 'tag' => e($asset->asset_tag)]));
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)]));
}
@@ -289,6 +293,7 @@ class AssetsController extends Controller
*/
public function update(ImageUploadRequest $request, $assetId = null) : RedirectResponse
{
// Check if the asset exists
if (! $asset = Asset::find($assetId)) {
// Redirect to the asset management page with error
@@ -331,7 +336,7 @@ class AssetsController extends Controller
$status = Statuslabel::find($asset->status_id);
if($status->archived){
if ($status && $status->archived) {
$asset->assigned_to = null;
}
@@ -350,14 +355,26 @@ class AssetsController extends Controller
}
// Update the asset data
$asset_tag = $request->input('asset_tags');
$serial = $request->input('serials');
$asset->serial = $request->input('serials');
if (is_array($request->input('serials'))) {
$asset->serial = $serial[1];
}
$asset->name = $request->input('name');
$asset->serial = $serial[1];
$asset->company_id = Company::getIdForCurrentUser($request->input('company_id'));
$asset->model_id = $request->input('model_id');
$asset->order_number = $request->input('order_number');
$asset->asset_tag = $asset_tag[1];
$asset_tags = $request->input('asset_tags');
$asset->asset_tag = $request->input('asset_tags');
if (is_array($request->input('asset_tags'))) {
$asset->asset_tag = $asset_tags[1];
}
$asset->notes = $request->input('notes');
$asset = $request->handleImages($asset);
@@ -369,6 +386,7 @@ class AssetsController extends Controller
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->field_encrypted == '1') {
if (Gate::allows('admin')) {
if (is_array($request->input($field->db_column))) {
@@ -387,9 +405,10 @@ class AssetsController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->save()) {
return redirect()->route('hardware.show', $assetId)
return redirect()->to(Helper::getRedirectOption($request, $assetId, 'Assets'))
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -459,9 +478,16 @@ class AssetsController extends Controller
$tag = $tag ? $tag : $request->get('assetTag');
$topsearch = ($request->get('topsearch') == 'true');
if (! $asset = Asset::where('asset_tag', '=', $tag)->first()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
// Search for an exact and unique asset tag match
$assets = Asset::where('asset_tag', '=', $tag);
// If not a unique result, redirect to the index view
if ($assets->count() != 1) {
return redirect()->route('hardware.index')
->with('search', $tag)
->with('warning', trans('admin/hardware/message.does_not_exist_var', [ 'asset_tag' => $tag ]));
}
$asset = $assets->first();
$this->authorize('view', $asset);
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
@@ -576,26 +602,20 @@ class AssetsController extends Controller
* @since [v1.0]
* @return \Illuminate\Contracts\View\View
*/
public function getClone($assetId = null)
public function getClone(Asset $asset)
{
// Check if the asset exists
if (is_null($asset_to_clone = Asset::find($assetId))) {
// Redirect to the asset management page
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('create', $asset_to_clone);
$asset = clone $asset_to_clone;
$asset->id = null;
$asset->asset_tag = '';
$asset->serial = '';
$asset->assigned_to = '';
$this->authorize('create', $asset);
$cloned = clone $asset;
$cloned->id = null;
$cloned->asset_tag = '';
$cloned->serial = '';
$cloned->assigned_to = '';
$cloned->deleted_at = '';
return view('hardware/edit')
->with('statuslabel_list', Helper::statusLabelList())
->with('statuslabel_types', Helper::statusTypeList())
->with('item', $asset);
->with('item', $cloned);
}
/**
@@ -824,7 +844,7 @@ class AssetsController extends Controller
{
$this->authorize('checkin', Asset::class);
return view('hardware/quickscan-checkin');
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
}
public function audit($id)
@@ -92,7 +92,9 @@ 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')->whereIn('assets.id', $asset_ids);
$assets = Asset::with('assignedTo', 'location', 'model')
->whereIn('assets.id', $asset_ids)
->withTrashed();
$assets = $assets->get();
@@ -225,7 +227,8 @@ class BulkAssetsController extends Controller
* its checkout status.
*/
if (($request->filled('purchase_date'))
if (($request->filled('name'))
|| ($request->filled('purchase_date'))
|| ($request->filled('expected_checkin'))
|| ($request->filled('purchase_cost'))
|| ($request->filled('supplier_id'))
@@ -237,6 +240,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('status_id'))
|| ($request->filled('model_id'))
|| ($request->filled('next_audit_date'))
|| ($request->filled('null_name'))
|| ($request->filled('null_purchase_date'))
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
@@ -249,13 +253,14 @@ class BulkAssetsController extends Controller
$this->update_array = [];
/**
* Leave out model_id and status here because we do math on that later. We have to do some extra
* validation and checks on those two.
* Leave out model_id and status here because we do math on that later. We have to do some
* extra validation and checks on those two.
*
* It's tempting to make these match the request check above, but some of these values require
* extra work to make sure the data makes sense.
*/
$this->conditionallyAddItem('purchase_date')
$this->conditionallyAddItem('name')
->conditionallyAddItem('purchase_date')
->conditionallyAddItem('expected_checkin')
->conditionallyAddItem('order_number')
->conditionallyAddItem('requestable')
@@ -269,6 +274,11 @@ class BulkAssetsController extends Controller
/**
* Blank out fields that were requested to be blanked out via checkbox
*/
if ($request->input('null_name')=='1') {
$this->update_array['name'] = null;
}
if ($request->input('null_purchase_date')=='1') {
$this->update_array['purchase_date'] = null;
}
@@ -483,12 +493,7 @@ class BulkAssetsController extends Controller
if ($request->filled('ids')) {
$assets = Asset::find($request->get('ids'));
foreach ($assets as $asset) {
$update_array['deleted_at'] = date('Y-m-d H:i:s');
$update_array['assigned_to'] = null;
DB::table('assets')
->where('id', $asset->id)
->update($update_array);
$asset->delete();
} // endforeach
return redirect($bulk_back_url)->with('success', trans('admin/hardware/message.delete.success'));
@@ -87,7 +87,7 @@ class ResetPasswordController extends Controller
'password.not_in' => trans('validation.disallow_same_pwd_as_user_fields'),
];
$request->validate($this->rules(), $request->all(), $this->validationErrorMessages());
$request->validate($this->rules());
Log::debug('Checking if '.$request->input('username').' exists');
// Check to see if the user even exists - we'll treat the response the same to prevent user sniffing
+20 -8
View File
@@ -99,12 +99,18 @@ class SamlController extends Controller
{
$saml = $this->saml;
$auth = $saml->getAuth();
$auth->processResponse();
$saml_exception = false;
try {
$auth->processResponse();
} catch (\Exception $e) {
Log::warning("Exception caught in SAML login: " . $e->getMessage());
$saml_exception = true;
}
$errors = $auth->getErrors();
if (! empty($errors)) {
Log::error('There was an error with SAML ACS: '.implode(', ', $errors));
Log::error('Reason: '.$auth->getLastErrorReason());
if (!empty($errors) || $saml_exception) {
Log::warning('There was an error with SAML ACS: ' . implode(', ', $errors));
Log::warning('Reason: ' . $auth->getLastErrorReason());
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
}
@@ -132,12 +138,18 @@ class SamlController extends Controller
{
$auth = $this->saml->getAuth();
$retrieveParametersFromServer = $this->saml->getSetting('retrieveParametersFromServer', false);
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
$saml_exception = false;
try {
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
} catch (\Exception $e) {
Log::warning("Exception caught in SAML single-logout: " . $e->getMessage());
$saml_exception = true;
}
$errors = $auth->getErrors();
if (! empty($errors)) {
Log::error('There was an error with SAML SLS: '.implode(', ', $errors));
Log::error('Reason: '.$auth->getLastErrorReason());
if (!empty($errors) || $saml_exception) {
Log::warning('There was an error with SAML SLS: ' . implode(', ', $errors));
Log::warning('Reason: ' . $auth->getLastErrorReason());
return view('errors.403');
}
+1 -1
View File
@@ -20,7 +20,7 @@ trait CheckInOutRequest
return Location::findOrFail(request('assigned_location'));
case 'asset':
return Asset::findOrFail(request('assigned_asset'));
case 'user':
default:
return User::findOrFail(request('assigned_user'));
}
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Components;
use App\Events\CheckoutableCheckedIn;
use App\Events\ComponentCheckedIn;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\Component;
@@ -96,12 +97,10 @@ class ComponentCheckinController extends Controller
$asset = Asset::find($component_assets->asset_id);
event(new CheckoutableCheckedIn($component, $asset, auth()->user(), $request->input('note'), Carbon::now()));
if ($backto == 'asset'){
return redirect()->route('hardware.show', $asset->id)->with('success',
trans('admin/components/message.checkin.success'));
}
return redirect()->route('components.index')->with('success',
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'));
}
@@ -4,9 +4,11 @@ namespace App\Http\Controllers\Components;
use App\Events\CheckoutableCheckedOut;
use App\Events\ComponentCheckedOut;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\Component;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
@@ -93,14 +95,18 @@ class ComponentCheckoutController extends Controller
->withInput();
}
// Check if the user exists
// Check if the asset exists
$asset = Asset::find($request->input('asset_id'));
if ((Setting::getSettings()->full_multiple_companies_support) && $component->company_id !== $asset->company_id) {
return redirect()->route('components.checkout.show', $componentId)->with('error', trans('general.error_user_company'));
}
// Update the component data
$component->asset_id = $request->input('asset_id');
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'user_id' => auth()->user(),
'user_id' => auth()->user()->id,
'created_at' => date('Y-m-d H:i:s'),
'assigned_qty' => $request->input('assigned_qty'),
'asset_id' => $request->input('asset_id'),
@@ -109,6 +115,11 @@ class ComponentCheckoutController extends Controller
event(new CheckoutableCheckedOut($component, $asset, auth()->user(), $request->input('note')));
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
$request->request->add(['checkout_to_type' => 'asset']);
$request->request->add(['assigned_asset' => $asset->id]);
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'));
}
}
@@ -86,8 +86,10 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
@@ -160,8 +162,10 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($component->save()) {
return redirect()->route('components.index')->with('success', trans('admin/components/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $component->id, 'Components'))->with('success', trans('admin/components/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($component->getErrors());
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Consumables;
use App\Events\CheckoutableCheckedOut;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Consumable;
use App\Models\User;
@@ -33,7 +34,7 @@ class ConsumableCheckoutController extends Controller
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0){
return redirect()->route('consumables.index')
->with('error', trans('admin/consumables/message.checkout.unavailable'));
->with('error', trans('admin/consumables/message.checkout.unavailable', ['requested' => 1, 'remaining' => $consumable->numRemaining()]));
}
// Return the checkout view
@@ -76,7 +77,7 @@ class ConsumableCheckoutController extends Controller
// Make sure there is at least one available to checkout
if ($consumable->numRemaining() <= 0 || $quantity > $consumable->numRemaining()) {
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable'));
return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.checkout.unavailable', ['requested' => $quantity, 'remaining' => $consumable->numRemaining() ]));
}
$admin_user = auth()->user();
@@ -101,7 +102,13 @@ class ConsumableCheckoutController extends Controller
}
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
$request->request->add(['checkout_to_type' => 'user']);
$request->request->add(['assigned_user' => $user->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
// Redirect to the new consumable page
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.checkout.success'));
}
}
@@ -87,8 +87,10 @@ class ConsumablesController extends Controller
$consumable = $request->handleImages($consumable);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -160,8 +162,10 @@ class ConsumablesController extends Controller
$consumable = $request->handleImages($consumable);
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($consumable->save()) {
return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.update.success'));
return redirect()->to(Helper::getRedirectOption($request, $consumable->id, 'Consumables'))->with('success', trans('admin/consumables/message.update.success'));
}
return redirect()->back()->withInput()->withErrors($consumable->getErrors());
@@ -200,7 +204,7 @@ class ConsumablesController extends Controller
*/
public function show($consumableId = null)
{
$consumable = Consumable::find($consumableId);
$consumable = Consumable::withCount('users as users_consumables')->find($consumableId);
$this->authorize($consumable);
if (isset($consumable->id)) {
return view('consumables/view', compact('consumable'));
@@ -209,4 +213,16 @@ class ConsumablesController extends Controller
return redirect()->route('consumables.index')
->with('error', trans('admin/consumables/message.does_not_exist'));
}
public function clone(Consumable $consumable) : View
{
$this->authorize('create', $consumable);
$consumable_to_close = $consumable;
$consumable = clone $consumable_to_close;
$consumable->id = null;
$consumable->image = null;
$consumable->user_id = null;
return view('consumables/edit')->with('item', $consumable);
}
}
@@ -211,7 +211,7 @@ class CustomFieldsetsController extends Controller
return redirect()->route('fieldsets.show', [$id])->with('success', trans('admin/custom_fields/message.field.create.assoc_success'));
}
return redirect()->route('fieldsets.show', [$id])->with('error', 'No field selected.');
return redirect()->route('fieldsets.show', [$id])->with('error', trans('admin/custom_fields/message.field.none_selected'));
}
/**
@@ -62,6 +62,20 @@ class DepreciationsController extends Controller
$depreciation->name = $request->input('name');
$depreciation->months = $request->input('months');
$depreciation->user_id = Auth::id();
$request->validate([
'depreciation_min' => [
'required',
'numeric',
function ($attribute, $value, $fail) use ($request) {
if ($request->input('depreciation_type') == 'percent' && ($value < 0 || $value > 100)) {
$fail(trans('validation.percent'));
}
},
],
'depreciation_type' => 'required|in:amount,percent',
]);
$depreciation->depreciation_type = $request->input('depreciation_type');
$depreciation->depreciation_min = $request->input('depreciation_min');
// Was the asset created?
@@ -116,6 +130,20 @@ class DepreciationsController extends Controller
// Depreciation data
$depreciation->name = $request->input('name');
$depreciation->months = $request->input('months');
$request->validate([
'depreciation_min' => [
'required',
'numeric',
function ($attribute, $value, $fail) use ($request) {
if ($request->input('depreciation_type') == 'percent' && ($value < 0 || $value > 100)) {
$fail(trans('validation.percent'));
}
},
],
'depreciation_type' => 'required|in:amount,percent',
]);
$depreciation->depreciation_type = $request->input('depreciation_type');
$depreciation->depreciation_min = $request->input('depreciation_min');
// Was the asset created?
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Licenses;
use App\Events\CheckoutableCheckedIn;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\License;
use App\Models\LicenseSeat;
@@ -100,15 +101,15 @@ class LicenseCheckinController extends Controller
$licenseSeat->asset_id = null;
$licenseSeat->notes = $request->input('notes');
session()->put(['redirect_option' => $request->get('redirect_option')]);
// Was the asset updated?
if ($licenseSeat->save()) {
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
if ($backTo == 'user') {
return redirect()->route('users.show', $return_to->id)->with('success', trans('admin/licenses/message.checkin.success'));
}
return redirect()->route('licenses.show', $licenseSeat->license_id)->with('success', trans('admin/licenses/message.checkin.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
}
// Redirect to the license page with error
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Licenses;
use App\Events\CheckoutableCheckedOut;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\LicenseCheckoutRequest;
use App\Models\Accessory;
@@ -81,10 +82,27 @@ class LicenseCheckoutController extends Controller
$checkoutMethod = 'checkoutTo'.ucwords(request('checkout_to_type'));
if ($this->$checkoutMethod($licenseSeat)) {
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.checkout.success'));
if ($request->filled('asset_id')) {
$checkoutTarget = $this->checkoutToAsset($licenseSeat);
$request->request->add(['assigned_asset' => $checkoutTarget->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'asset']);
} elseif ($request->filled('assigned_to')) {
$checkoutTarget = $this->checkoutToUser($licenseSeat);
$request->request->add(['assigned_user' => $checkoutTarget->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'user']);
}
if ($checkoutTarget) {
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkout.success'));
}
return redirect()->route('licenses.index')->with('error', trans('Something went wrong handling this checkout.'));
}
@@ -94,14 +112,14 @@ class LicenseCheckoutController extends Controller
if (! $licenseSeat) {
if ($seatId) {
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'This Seat is not available for checkout.'));
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.unavailable')));
}
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'There are no available seats for this license.'));
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats')));
}
if (! $licenseSeat->license->is($license)) {
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', 'The license seat provided does not match the license.'));
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.mismatch')));
}
return $licenseSeat;
@@ -120,8 +138,7 @@ class LicenseCheckoutController extends Controller
}
if ($licenseSeat->save()) {
event(new CheckoutableCheckedOut($licenseSeat, $target, auth()->user(), request('notes')));
return true;
return $target;
}
return false;
@@ -137,8 +154,7 @@ class LicenseCheckoutController extends Controller
if ($licenseSeat->save()) {
event(new CheckoutableCheckedOut($licenseSeat, $target, auth()->user(), request('notes')));
return true;
return $target;
}
return false;
@@ -102,8 +102,10 @@ class LicensesController extends Controller
$license->user_id = Auth::id();
$license->min_amt = $request->input('min_amt');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.create.success'));
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($license->getErrors());
@@ -180,8 +182,10 @@ class LicensesController extends Controller
$license->category_id = $request->input('category_id');
$license->min_amt = $request->input('min_amt');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($license->save()) {
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
return redirect()->to(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());
@@ -49,6 +49,7 @@ class ProfileController extends Controller
$user->gravatar = $request->input('gravatar');
$user->skin = $request->input('skin');
$user->phone = $request->input('phone');
$user->enable_sounds = $request->input('enable_sounds', false);
if (! config('app.lock_passwords')) {
$user->locale = $request->input('locale', 'en-US');
+17 -25
View File
@@ -14,7 +14,6 @@ use App\Models\Asset;
use App\Models\User;
use App\Notifications\FirstAdminNotification;
use App\Notifications\MailTest;
use Illuminate\Http\Client\HttpClientException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage;
@@ -129,11 +128,11 @@ class SettingsController extends Controller
protected function dotEnvFileIsExposed() : bool
{
try {
return Http::timeout(10)
return Http::withoutVerifying()->timeout(10)
->accept('*/*')
->get(URL::to('.env'))
->successful();
} catch (HttpClientException $e) {
} catch (\Exception $e) {
Log::debug($e->getMessage());
return true;
}
@@ -325,6 +324,7 @@ class SettingsController extends Controller
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
$setting->unique_serial = $request->input('unique_serial', '0');
$setting->shortcuts_enabled = $request->input('shortcuts_enabled', '0');
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
$setting->dashboard_message = $request->input('dashboard_message');
@@ -414,10 +414,7 @@ class SettingsController extends Controller
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
if ($request->input('clear_logo') == '1') {
if (($setting->logo) && (Storage::exists($setting->logo))) {
Storage::disk('public')->delete($setting->logo);
}
$setting = $request->deleteExistingImage($setting, '', 'logo');
$setting->logo = null;
$setting->brand = 1;
}
@@ -425,43 +422,38 @@ class SettingsController extends Controller
// Email logo upload
$setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo');
if ($request->input('clear_email_logo') == '1') {
if (($setting->email_logo) && (Storage::exists($setting->email_logo))) {
Storage::disk('public')->delete($setting->email_logo);
}
$setting = $request->deleteExistingImage($setting, '', 'email_logo');
$setting->email_logo = null;
// If they are uploading an image, validate it and upload it
}
// Label logo upload
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
if ($request->input('clear_label_logo') == '1') {
if (($setting->label_logo) && (Storage::exists($setting->label_logo))) {
Storage::disk('public')->delete($setting->label_logo);
}
if ($request->input('clear_label_logo') == '1') {
$setting = $request->deleteExistingImage($setting, '', 'label_logo');
$setting->label_logo = null;
}
// Favicon upload
$setting = $request->handleImages($setting, 100, 'favicon', '', 'favicon');
if ('1' == $request->input('clear_favicon')) {
if (($setting->favicon) && (Storage::exists($setting->favicon))) {
Storage::disk('public')->delete($setting->favicon);
}
$setting = $request->deleteExistingImage($setting, '', 'favicon');
$setting->favicon = null;
}
// Default avatar upload
$setting = $request->handleImages($setting, 500, 'default_avatar', 'avatars', 'default_avatar');
if ($request->input('clear_default_avatar') == '1') {
if (($setting->default_avatar) && (Storage::exists('avatars/'.$setting->default_avatar))) {
Storage::disk('public')->delete('avatars/'.$setting->default_avatar);
if ($request->input('clear_default_avatar') == '1') {
// Don't delete the file, just update the field if this is the default
if ($setting->default_avatar!='default.png') {
$setting = $request->deleteExistingImage($setting, 'avatars', 'default_avatar');
}
$setting->default_avatar = null;
}
if ($request->input('restore_default_avatar') == '1') {
$setting->default_avatar = 'default.png';
}
}
if ($setting->save()) {
@@ -1264,7 +1256,7 @@ class SettingsController extends Controller
DB::table('users')->update(['remember_token' => null]);
Auth::logout();
return redirect()->route('login')->with('success', 'Your system has been restored. Please login again.');
return redirect()->route('login')->with('success', trans('admin/settings/message.restore.success'));
} else {
return redirect()->route('settings.backups.index')->with('error', trans('admin/settings/message.backup.file_not_found'));
}
@@ -16,6 +16,7 @@ use App\Models\Consumable;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Password;
@@ -218,21 +219,19 @@ class BulkUsersController extends Controller
}
$users = User::whereIn('id', $user_raw_array)->get();
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', \App\Models\User::class)->get();
$accessories = DB::table('accessories_users')->whereIn('assigned_to', $user_raw_array)->get();
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', User::class)->get();
$accessoryUserRows = DB::table('accessories_checkout')->where('assigned_type', User::class)->whereIn('assigned_to', $user_raw_array)->get();
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
$consumableUserRows = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
if ((($assets->count() > 0) && ((!$request->filled('status_id')) || ($request->input('status_id') == '')))) {
return redirect()->route('users.index')->with('error', 'No status selected');
}
$this->logItemCheckinAndDelete($assets, Asset::class);
$this->logItemCheckinAndDelete($accessories, Accessory::class);
$this->logAccessoriesCheckin($accessoryUserRows);
$this->logItemCheckinAndDelete($licenses, License::class);
$this->logItemCheckinAndDelete($consumables, Consumable::class);
$this->logConsumablesCheckin($consumableUserRows);
Asset::whereIn('id', $assets->pluck('id'))->update([
'status_id' => e(request('status_id')),
@@ -241,19 +240,14 @@ class BulkUsersController extends Controller
'expected_checkin' => null,
]);
LicenseSeat::whereIn('id', $licenses->pluck('id'))->update(['assigned_to' => null]);
ConsumableAssignment::whereIn('id', $consumables->pluck('id'))->delete();
ConsumableAssignment::whereIn('id', $consumableUserRows->pluck('id'))->delete();
foreach ($users as $user) {
$user->consumables()->sync([]);
$user->accessories()->sync([]);
if ($request->input('delete_user')=='1') {
$user->delete();
}
}
$msg = trans('general.bulk_checkin_success');
@@ -279,7 +273,7 @@ class BulkUsersController extends Controller
if ($itemType == License::class){
$item_id = $item->license_id;
}
$logAction->item_id = $item_id;
// We can't rely on get_class here because the licenses/accessories fetched above are not eloquent models, but simply arrays.
$logAction->item_type = $itemType;
@@ -291,6 +285,34 @@ class BulkUsersController extends Controller
}
}
private function logAccessoriesCheckin(Collection $accessoryUserRows): void
{
foreach ($accessoryUserRows as $accessoryUserRow) {
$logAction = new Actionlog();
$logAction->item_id = $accessoryUserRow->accessory_id;
$logAction->item_type = Accessory::class;
$logAction->target_id = $accessoryUserRow->assigned_to;
$logAction->target_type = User::class;
$logAction->user_id = Auth::id();
$logAction->note = 'Bulk checkin items';
$logAction->logaction('checkin from');
}
}
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->user_id = Auth::id();
$logAction->note = 'Bulk checkin items';
$logAction->logaction('checkin from');
}
}
/**
* Save bulk-edited users
*
@@ -133,6 +133,8 @@ class UsersController extends Controller
// we have to invoke the
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($user->save()) {
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
@@ -152,7 +154,7 @@ class UsersController extends Controller
$user->notify(new WelcomeNotification($data));
}
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.create'));
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -309,10 +311,11 @@ class UsersController extends Controller
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($user->save()) {
// Redirect to the user page
return redirect()->route('users.index')
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
->with('success', trans('admin/users/message.success.update'));
}
+2
View File
@@ -43,10 +43,12 @@ class Kernel extends HttpKernel
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
\App\Http\Middleware\AssetCountForSidebar::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'auth:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
+1 -1
View File
@@ -20,5 +20,5 @@ class EncryptCookies extends BaseEncrypter
*
* @var bool
*/
protected static $serialize = true;
protected static $serialize = false;
}
@@ -0,0 +1,76 @@
<?php
namespace App\Http\Requests;
use App\Models\Accessory;
use Illuminate\Support\Facades\Gate;
class AccessoryCheckoutRequest extends ImageUploadRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('checkout', new Accessory);
}
public function prepareForValidation(): void
{
if ($this->accessory) {
$this->diff = ($this->accessory->numRemaining() - $this->checkout_qty);
$this->merge([
'checkout_qty' => $this->checkout_qty ?? 1,
'number_remaining_after_checkout' => (int) ($this->accessory->numRemaining() - $this->checkout_qty),
'number_currently_remaining' => (int) $this->accessory->numRemaining(),
'checkout_difference' => (int) $this->diff,
]);
\Log::debug('---------------------------------------------');
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return array_merge(
[
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
'number_remaining_after_checkout' => [
'min:0',
'required',
'integer',
],
'checkout_qty' => [
'integer',
'lte:number_currently_remaining',
'min:1',
],
],
);
}
public function messages(): array
{
$messages = [
'checkout_qty.lte' => trans_choice('admin/accessories/message.checkout.checkout_qty.lte', $this->number_currently_remaining, [
'number_currently_remaining' => $this->number_currently_remaining,
'checkout_qty' => $this->checkout_qty,
]),
];
return $messages;
}
}
+13 -12
View File
@@ -10,6 +10,7 @@ use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Exception\NotReadableException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
class ImageUploadRequest extends Request
{
@@ -123,7 +124,7 @@ class ImageUploadRequest extends Request
} catch(NotReadableException $e) {
Log::debug($e);
$validator = \Validator::make([], []);
$validator = Validator::make([], []);
$validator->errors()->add($form_fieldname, trans('general.unaccepted_image_type', ['mimetype' => $image->getClientMimeType()]));
throw new \Illuminate\Validation\ValidationException($validator);
@@ -135,28 +136,28 @@ class ImageUploadRequest extends Request
}
// Remove Current image if exists
if (($item->{$form_fieldname}!='') && (Storage::disk('public')->exists($path.'/'.$item->{$db_fieldname}))) {
try {
Storage::disk('public')->delete($path.'/'.$item->{$form_fieldname});
} catch (\Exception $e) {
Log::debug('Could not delete old file. '.$path.'/'.$file_name.' does not exist?');
}
}
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
$item->{$db_fieldname} = $file_name;
}
// If the user isn't uploading anything new but wants to delete their old image, do so
} elseif ($this->input('image_delete') == '1') {
Log::debug('Deleting image');
$item = $this->deleteExistingImage($item, $path, $db_fieldname);
}
return $item;
}
public function deleteExistingImage($item, $path = null, $db_fieldname = 'image') {
if ($item->{$db_fieldname}!='') {
try {
Storage::disk('public')->delete($path.'/'.$item->{$db_fieldname});
$item->{$db_fieldname} = null;
$item->{$db_fieldname} = null;
} catch (\Exception $e) {
Log::debug($e);
}
}
return $item;
@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests;
use App\Models\Accessory;
use App\Models\Category;
use Illuminate\Support\Facades\Gate;
class StoreAccessoryRequest extends ImageUploadRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('create', new Accessory);
}
public function prepareForValidation(): void
{
if ($this->category_id) {
if ($category = Category::find($this->category_id)) {
$this->merge([
'category_type' => $category->category_type ?? null,
]);
}
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return array_merge(
['category_type' => 'in:accessory'],
parent::rules(),
);
}
public function messages(): array
{
$messages = ['category_type.in' => trans('admin/accessories/message.invalid_category_type')];
return $messages;
}
public function response(array $errors)
{
return $this->redirector->back()->withInput()->withErrors($errors, $this->errorBag);
}
}
+2
View File
@@ -2,6 +2,7 @@
namespace App\Http\Requests;
use App\Http\Requests\Traits\MayContainCustomFields;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
@@ -11,6 +12,7 @@ use Illuminate\Support\Facades\Gate;
class StoreAssetRequest extends ImageUploadRequest
{
use MayContainCustomFields;
/**
* Determine if the user is authorized to make this request.
*
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Requests\Traits;
use App\Models\AssetModel;
use App\Models\CustomField;
trait MayContainCustomFields
{
// this gets called automatically on a form request
public function withValidator($validator)
{
// find the model
if ($this->method() == 'POST') {
$asset_model = AssetModel::find($this->model_id);
}
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
$asset_model = $this->asset->model;
}
// collect the custom fields in the request
$validator->after(function ($validator) use ($asset_model) {
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
return str_starts_with($attributes, '_snipeit_');
});
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
if (count($request_fields) > 0) {
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
->each(function ($request_field_name) use ($request_fields, $validator) {
if (CustomField::where('db_column', $request_field_name)->exists()) {
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found_on_model'));
} else {
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found'));
}
});
}
});
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests;
use App\Http\Requests\Traits\MayContainCustomFields;
use App\Models\Asset;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class UpdateAssetRequest extends ImageUploadRequest
{
use MayContainCustomFields;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Gate::allows('update', $this->asset);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = array_merge(
parent::rules(),
(new Asset)->getRules(),
// this is to overwrite rulesets that include required, and rewrite unique_undeleted
[
'model_id' => ['integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
'status_id' => ['integer', 'exists:status_labels,id'],
'asset_tag' => [
'min:1', 'max:255', 'not_array',
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed()
],
],
);
return $rules;
}
}
@@ -39,7 +39,7 @@ class AccessoriesTransformer
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
'remaining_qty' => (int) $accessory->numRemaining(),
'users_count' => $accessory->users_count,
'checkouts_count' => $accessory->checkouts_count,
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
@@ -66,27 +66,42 @@ class AccessoriesTransformer
return $array;
}
public function transformCheckedoutAccessory($accessory, $accessory_users, $total)
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
{
$array = [];
foreach ($accessory_users as $user) {
foreach ($accessory_checkouts as $checkout) {
$array[] = [
'assigned_pivot_id' => $user->pivot->id,
'id' => (int) $user->id,
'username' => e($user->username),
'name' => e($user->getFullNameAttribute()),
'first_name'=> e($user->first_name),
'last_name'=> e($user->last_name),
'employee_number' => e($user->employee_num),
'checkout_notes' => e($user->pivot->note),
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
'type' => 'user',
'id' => $checkout->id,
'assigned_to' => $this->transformAssignedTo($checkout),
'checkout_notes' => e($checkout->note),
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
'available_actions' => ['checkin' => true],
];
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformAssignedTo($accessoryCheckout)
{
if ($accessoryCheckout->checkedOutToUser()) {
return [
'id' => (int) $accessoryCheckout->assigned->id,
'username' => e($accessoryCheckout->assigned->username),
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
'first_name'=> e($accessoryCheckout->assigned->first_name),
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
'type' => 'user',
];
}
return $accessoryCheckout->assigned ? [
'id' => $accessoryCheckout->assigned->id,
'name' => e($accessoryCheckout->assigned->display_name),
'type' => $accessoryCheckout->assignedType(),
] : null;
}
}
@@ -205,11 +205,11 @@ class ActionlogsTransformer
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
public function transformCheckedoutActionlog (Collection $accessories_checkout, $total)
{
$array = array();
foreach ($accessories_users as $user) {
foreach ($accessories_checkout as $user) {
$array[] = (new UsersTransformer)->transformUser($user);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
@@ -256,7 +256,7 @@ class ActionlogsTransformer
$clean_meta['rtd_location_id']['old'] = $clean_meta['rtd_location_id']['old'] ? "[id: ".$clean_meta['rtd_location_id']['old']."] ". $oldRtdName : '';
$clean_meta['rtd_location_id']['new'] = $clean_meta['rtd_location_id']['new'] ? "[id: ".$clean_meta['rtd_location_id']['new']."] ". $newRtdName : '';
$clean_meta['Default Location'] = $clean_meta['rtd_location_id'];
$clean_meta[trans('admin/hardware/form.default_location')] = $clean_meta['rtd_location_id'];
unset($clean_meta['rtd_location_id']);
}
@@ -272,7 +272,7 @@ class ActionlogsTransformer
$clean_meta['location_id']['old'] = $clean_meta['location_id']['old'] ? "[id: ".$clean_meta['location_id']['old']."] ". $oldLocationName : '';
$clean_meta['location_id']['new'] = $clean_meta['location_id']['new'] ? "[id: ".$clean_meta['location_id']['new']."] ". $newLocationName : '';
$clean_meta['Current Location'] = $clean_meta['location_id'];
$clean_meta[trans('admin/locations/message.current_location')] = $clean_meta['location_id'];
unset($clean_meta['location_id']);
}
@@ -287,7 +287,7 @@ class ActionlogsTransformer
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName;
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */
$clean_meta['Model'] = $clean_meta['model_id'];
$clean_meta[trans('admin/hardware/form.model')] = $clean_meta['model_id'];
unset($clean_meta['model_id']);
}
if(array_key_exists('company_id', $clean_meta)) {
@@ -300,7 +300,7 @@ class ActionlogsTransformer
$clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned');
$clean_meta['company_id']['new'] = $clean_meta['company_id']['new'] ? "[id: ".$clean_meta['company_id']['new']."] ". $newCompanyName : trans('general.unassigned');
$clean_meta['Company'] = $clean_meta['company_id'];
$clean_meta[trans('general.company')] = $clean_meta['company_id'];
unset($clean_meta['company_id']);
}
if(array_key_exists('supplier_id', $clean_meta)) {
@@ -313,7 +313,7 @@ class ActionlogsTransformer
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned');
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned');
$clean_meta['Supplier'] = $clean_meta['supplier_id'];
$clean_meta[trans('general.supplier')] = $clean_meta['supplier_id'];
unset($clean_meta['supplier_id']);
}
if(array_key_exists('status_id', $clean_meta)) {
@@ -326,11 +326,11 @@ class ActionlogsTransformer
$clean_meta['status_id']['old'] = $clean_meta['status_id']['old'] ? "[id: ".$clean_meta['status_id']['old']."] ". $oldStatusName : trans('general.unassigned');
$clean_meta['status_id']['new'] = $clean_meta['status_id']['new'] ? "[id: ".$clean_meta['status_id']['new']."] ". $newStatusName : trans('general.unassigned');
$clean_meta['Status'] = $clean_meta['status_id'];
$clean_meta[trans('general.status_label')] = $clean_meta['status_id'];
unset($clean_meta['status_id']);
}
if(array_key_exists('asset_eol_date', $clean_meta)) {
$clean_meta['EOL date'] = $clean_meta['asset_eol_date'];
$clean_meta[trans('admin/hardware/form.eol_date')] = $clean_meta['asset_eol_date'];
unset($clean_meta['asset_eol_date']);
}
@@ -55,6 +55,7 @@ class ConsumablesTransformer
'checkin' => Gate::allows('checkin', Consumable::class),
'update' => Gate::allows('update', Consumable::class),
'delete' => Gate::allows('delete', Consumable::class),
'clone' => (Gate::allows('create', Consumable::class) && ($consumable->deleted_at == '')),
];
$array += $permissions_array;
@@ -7,6 +7,7 @@ use App\Models\Depreciable;
use App\Models\Depreciation;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
class DepreciationsTransformer
{
@@ -26,7 +27,7 @@ class DepreciationsTransformer
'id' => (int) $depreciation->id,
'name' => e($depreciation->name),
'months' => $depreciation->months.' '.trans('general.months'),
'depreciation_min' => $depreciation->depreciation_min,
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime')
];
@@ -51,7 +51,7 @@ class LicenseSeatsTransformer
];
if ($seat_count != 0) {
$array['name'] = 'Seat '.$seat_count;
$array['name'] = trans('admin/licenses/general.seat_count', ['count' => $seat_count]);
}
$permissions_array['available_actions'] = [
@@ -45,6 +45,10 @@ class LicensesTransformer
'maintained' => ($license->maintained == 1) ? true : false,
'supplier' => ($license->supplier) ? ['id' => (int) $license->supplier->id, 'name'=> e($license->supplier->name)] : null,
'category' => ($license->category) ? ['id' => (int) $license->category->id, 'name'=> e($license->category->name)] : null,
'created_by' => ($license->adminuser) ? [
'id' => (int) $license->adminuser->id,
'name'=> e($license->adminuser->present()->fullName()),
] : null,
'created_at' => Helper::getFormattedDateObject($license->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($license->updated_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($license->deleted_at, 'datetime'),
+1 -5
View File
@@ -3,14 +3,10 @@
namespace App\Importer;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Statuslabel;
use App\Models\User;
use App\Events\CheckoutableCheckedIn;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Crypt;
class AssetImporter extends ItemImporter
{
+19
View File
@@ -67,6 +67,7 @@ class UserImporter extends ItemImporter
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;
$this->handleEmptyStringsForDates();
$user_department = trim($this->findCsvMatch($row, 'department'));
if ($this->shouldUpdateField($user_department)) {
@@ -179,4 +180,22 @@ class UserImporter extends ItemImporter
{
$this->send_welcome = $send;
}
/**
* Since the findCsvMatch() method will set '' for columns that are present but empty,
* we need to set those empty strings to null to avoid passing bad data to the database
* (ie ending up with 0000-00-00 instead of the intended null).
*
* @return void
*/
private function handleEmptyStringsForDates(): void
{
if ($this->item['start_date'] === '') {
$this->item['start_date'] = null;
}
if ($this->item['end_date'] === '') {
$this->item['end_date'] = null;
}
}
}
+5 -1
View File
@@ -137,7 +137,11 @@ class CheckoutableListener
*/
private function getCheckoutAcceptance($event)
{
if (! $event->checkoutable->requireAcceptance()) {
$checkedOutToType = get_class($event->checkedOutTo);
if ($checkedOutToType != "App\Models\User") {
return null;
}
if (!$event->checkoutable->requireAcceptance()) {
return null;
}
+26 -10
View File
@@ -63,7 +63,7 @@ class Accessory extends SnipeModel
'company_id' => 'integer|nullable',
'min_amt' => 'integer|min:0|nullable',
'purchase_cost' => 'numeric|nullable|gte:0',
'purchase_date' => 'date_format:Y-m-d|nullable',
'purchase_date' => 'date_format:Y-m-d|nullable',
];
@@ -253,9 +253,10 @@ class Accessory extends SnipeModel
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function users()
public function checkouts()
{
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id', 'created_at', 'note')->withTrashed();
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
->with('assignedTo');
}
/**
@@ -267,7 +268,9 @@ class Accessory extends SnipeModel
*/
public function hasUsers()
{
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->count();
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
->where('assigned_type', User::class)
->count();
}
/**
@@ -329,11 +332,24 @@ class Accessory extends SnipeModel
}
/**
* Check how many items within an accessory are checked out
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v5.0]
* @return int
*/
public function numCheckedOut()
{
return $this->checkouts_count ?? $this->checkouts()->count();
}
/**
* Check how many items of an accessory remain.
*
* In order to use this model method, you MUST call withCount('users as users_count')
* on the eloquent query in the controller, otherwise $this->>users_count will be null and
* In order to use this model method, you MUST call withCount('checkouts as checkouts_count')
* on the eloquent query in the controller, otherwise $this->checkouts_count will be null and
* bad things happen.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -342,11 +358,11 @@ class Accessory extends SnipeModel
*/
public function numRemaining()
{
$checkedout = $this->users_count;
$checkedout = $this->numCheckedOut();
$total = $this->qty;
$remaining = $total - $checkedout;
return (int) $remaining;
return $remaining;
}
/**
@@ -357,12 +373,12 @@ class Accessory extends SnipeModel
*/
public function declinedCheckout(User $declinedBy, $signature)
{
if (is_null($accessory_user = \DB::table('accessories_users')->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
if (is_null($accessory_checkout = AccessoryCheckout::userAssigned()->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
}
$accessory_user->limit(1)->delete();
$accessory_checkout->limit(1)->delete();
}
/**
+148
View File
@@ -0,0 +1,148 @@
<?php
namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
/**
* Model for Accessories.
*
* @version v1.0
*/
class AccessoryCheckout extends Model
{
use Searchable;
protected $fillable = ['user_id', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
protected $table = 'accessories_checkout';
/**
* Establishes the accessory checkout -> accessory relationship
*
* @author [A. Kroeger]
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function accessory()
{
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
}
/**
* Establishes the accessory checkout -> user relationship
*
* @author [A. Kroeger]
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
{
return $this->hasOne(\App\Models\User::class, 'user_id');
}
/**
* Get the target this asset is checked out to
*
* @author [A. Kroeger]
* @since [v7.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedTo()
{
return $this->morphTo('assigned', 'assigned_type', 'assigned_to')->withTrashed();
}
/**
* Gets the lowercased name of the type of target the asset is assigned to
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return string
*/
public function assignedType()
{
return $this->assigned_type ? strtolower(class_basename($this->assigned_type)) : null;
}
/**
* Determines whether the accessory is checked out to a user
*
* Even though we allow allow for checkout to things beyond users
* this method is an easy way of seeing if we are checked out to a user.
*
* @author [A. Kroeger]
* @since [v7.0]
*/
public function checkedOutToUser(): bool
{
return $this->assignedType() === Asset::USER;
}
public function scopeUserAssigned(Builder $query): void
{
$query->where('assigned_type', '=', User::class);
}
/**
* Run additional, advanced searches.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $terms The search terms
* @return \Illuminate\Database\Eloquent\Builder
*/
public function advancedTextSearch(Builder $query, array $terms)
{
$userQuery = User::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
}
})->select('id');
$locationQuery = Location::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
})->select('id');
$assetQuery = Asset::where(function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
})->select('id');
$query->where(function ($query) use ($userQuery) {
$query->where('assigned_type', User::class)
->whereIn('assigned_to', $userQuery);
})->orWhere(function($query) use ($locationQuery) {
$query->where('assigned_type', Location::class)
->whereIn('assigned_to', $locationQuery);
})->orWhere(function($query) use ($assetQuery) {
$query->where('assigned_type', Asset::class)
->whereIn('assigned_to', $assetQuery);
})->orWhere(function($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('note', 'like', $search_str);
}
});
return $query;
}
}
+7 -1
View File
@@ -7,7 +7,6 @@ use App\Presenters\Presentable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
/**
* Model for the Actionlog (the table that keeps a historical log of
@@ -17,10 +16,12 @@ use Illuminate\Support\Facades\Auth;
*/
class Actionlog extends SnipeModel
{
use CompanyableTrait;
use HasFactory;
// This is to manually set the source (via setActionSource()) for determineActionSource()
protected ?string $source = null;
protected $with = ['admin'];
protected $presenter = \App\Presenters\ActionlogPresenter::class;
use SoftDeletes;
@@ -372,4 +373,9 @@ class Actionlog extends SnipeModel
{
$this->source = $source;
}
public function scopeOrderAdmin($query, $order)
{
return $query->leftJoin('users as admin_sort', 'action_logs.user_id', '=', 'admin_sort.id')->select('action_logs.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
}
}
+31 -28
View File
@@ -2,7 +2,6 @@
namespace App\Models;
use App\Events\AssetCheckedOut;
use App\Events\CheckoutableCheckedOut;
use App\Exceptions\CheckoutNotAllowed;
use App\Helpers\Helper;
@@ -31,6 +30,7 @@ class Asset extends Depreciable
{
protected $presenter = AssetPresenter::class;
protected $with = ['model', 'admin'];
use CompanyableTrait;
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
@@ -97,35 +97,38 @@ class Asset extends Depreciable
];
protected $rules = [
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
'status_id' => 'required|integer|exists:status_labels,id',
'asset_tag' => 'required|min:1|max:255|unique_undeleted:assets,asset_tag|not_array',
'name' => 'nullable|max:255',
'company_id' => 'nullable|integer|exists:companies,id',
'warranty_months' => 'nullable|numeric|digits_between:0,240',
'last_checkout' => 'nullable|date_format:Y-m-d H:i:s',
'last_checkin' => 'nullable|date_format:Y-m-d H:i:s',
'expected_checkin' => 'nullable|date',
'last_audit_date' => 'nullable|date_format:Y-m-d H:i:s',
// 'next_audit_date' => 'nullable|date|after:last_audit_date',
'next_audit_date' => 'nullable|date',
'location_id' => 'nullable|exists:locations,id',
'rtd_location_id' => 'nullable|exists:locations,id',
'purchase_date' => 'nullable|date|date_format:Y-m-d',
'serial' => 'nullable|unique_undeleted:assets,serial',
'purchase_cost' => 'nullable|numeric|gte:0',
'supplier_id' => 'nullable|exists:suppliers,id',
'asset_eol_date' => 'nullable|date',
'eol_explicit' => 'nullable|boolean',
'byod' => 'nullable|boolean',
'order_number' => 'nullable|string|max:191',
'notes' => 'nullable|string|max:65535',
'assigned_to' => 'nullable|integer',
'requestable' => 'nullable|boolean',
'model_id' => ['required', 'integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
'status_id' => ['required', 'integer', 'exists:status_labels,id'],
'asset_tag' => ['required', 'min:1', 'max:255', 'unique_undeleted:assets,asset_tag', 'not_array'],
'name' => ['nullable', 'max:255'],
'company_id' => ['nullable', 'integer', 'exists:companies,id'],
'warranty_months' => ['nullable', 'numeric', 'digits_between:0,240'],
'last_checkout' => ['nullable', 'date_format:Y-m-d H:i:s'],
'last_checkin' => ['nullable', 'date_format:Y-m-d H:i:s'],
'expected_checkin' => ['nullable', 'date'],
'last_audit_date' => ['nullable', 'date_format:Y-m-d H:i:s'],
'next_audit_date' => ['nullable', 'date'],
//'after:last_audit_date'],
'location_id' => ['nullable', 'exists:locations,id'],
'rtd_location_id' => ['nullable', 'exists:locations,id'],
'purchase_date' => ['nullable', 'date', 'date_format:Y-m-d'],
'serial' => ['nullable', 'unique_undeleted:assets,serial'],
'purchase_cost' => ['nullable', 'numeric', 'gte:0'],
'supplier_id' => ['nullable', 'exists:suppliers,id'],
'asset_eol_date' => ['nullable', 'date'],
'eol_explicit' => ['nullable', 'boolean'],
'byod' => ['nullable', 'boolean'],
'order_number' => ['nullable', 'string', 'max:191'],
'notes' => ['nullable', 'string', 'max:65535'],
'assigned_to' => ['nullable', 'integer'],
'requestable' => ['nullable', 'boolean'],
'assigned_user' => ['nullable', 'exists:users,id,deleted_at,NULL'],
'assigned_location' => ['nullable', 'exists:locations,id,deleted_at,NULL'],
'assigned_asset' => ['nullable', 'exists:assets,id,deleted_at,NULL']
];
/**
/**
* The attributes that are mass assignable.
*
* @var array
@@ -713,7 +716,7 @@ class Asset extends Depreciable
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function adminuser()
public function admin()
{
return $this->belongsTo(\App\Models\User::class, 'user_id');
}
+5 -1
View File
@@ -205,7 +205,11 @@ class Component extends SnipeModel
public function numCheckedOut()
{
$checkedout = 0;
foreach ($this->assets as $checkout) {
// In case there are elements checked out to assets that belong to a different company
// than this asset and full multiple company support is on we'll remove the global scope,
// so they are included in the count.
foreach ($this->assets()->withoutGlobalScope(new CompanyableScope)->get() as $checkout) {
$checkedout += $checkout->pivot->assigned_qty;
}
+1 -1
View File
@@ -7,7 +7,7 @@ use EasySlugger\Utf8Slugger;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule;
use Schema;
use Illuminate\Support\Facades\Schema;
use Watson\Validating\ValidatingTrait;
class CustomField extends Model
{
+28 -13
View File
@@ -76,9 +76,9 @@ class Depreciable extends SnipeModel
if ($months_passed >= $this->get_depreciation()->months){
//if there is a floor use it
if(!$this->get_depreciation()->depreciation_min == null) {
if($this->get_depreciation()->depreciation_min) {
$current_value = $this->get_depreciation()->depreciation_min;
$current_value = $this->calculateDepreciation();
}else{
$current_value = 0;
@@ -86,7 +86,7 @@ class Depreciable extends SnipeModel
}
else {
// The equation here is (Purchase_Cost-Floor_min)*(Months_passed/Months_til_depreciated)
$current_value = round(($this->purchase_cost-($this->purchase_cost - ($this->get_depreciation()->depreciation_min)) * ($months_passed / $this->get_depreciation()->months)), 2);
$current_value = round(($this->purchase_cost-($this->purchase_cost - ($this->calculateDepreciation())) * ($months_passed / $this->get_depreciation()->months)), 2);
}
@@ -95,7 +95,7 @@ class Depreciable extends SnipeModel
public function getMonthlyDepreciation(){
return ($this->purchase_cost-$this->get_depreciation()->depreciation_min)/$this->get_depreciation()->months;
return ($this->purchase_cost-$this->calculateDepreciation())/$this->get_depreciation()->months;
}
@@ -158,17 +158,20 @@ class Depreciable extends SnipeModel
public function time_until_depreciated()
{
// @link http://www.php.net/manual/en/class.datetime.php
$d1 = new \DateTime();
$d2 = $this->depreciated_date();
if ($this->depreciated_date()) {
// @link http://www.php.net/manual/en/class.datetime.php
$d1 = new \DateTime();
$d2 = $this->depreciated_date();
// @link http://www.php.net/manual/en/class.dateinterval.php
$interval = $d1->diff($d2);
if (! $interval->invert) {
return $interval;
} else {
return new \DateInterval('PT0S'); //null interval (zero seconds from now)
// @link http://www.php.net/manual/en/class.dateinterval.php
$interval = $d1->diff($d2);
if (! $interval->invert) {
return $interval;
} else {
return new \DateInterval('PT0S'); //null interval (zero seconds from now)
}
}
return false;
}
public function depreciated_date()
@@ -188,4 +191,16 @@ class Depreciable extends SnipeModel
{
return new \DateTime($time);
}
private function calculateDepreciation()
{
if($this->get_depreciation()->depreciation_type === 'percent') {
$depreciation_percent= $this->get_depreciation()->depreciation_min / 100;
$depreciation_min= $this->purchase_cost * $depreciation_percent;
return $depreciation_min;
}
$depreciation_min = $this->get_depreciation()->depreciation_min;
return $depreciation_min;
}
}
+14 -1
View File
@@ -50,7 +50,7 @@ class License extends Depreciable
'category_id' => 'required|exists:categories,id',
'company_id' => 'integer|nullable',
'purchase_cost'=> 'numeric|nullable|gte:0',
'purchase_date' => 'date_format:Y-m-d|nullable|max:10',
'purchase_date' => 'date_format:Y-m-d|nullable|max:10|required_with:depreciation_id',
'expiration_date' => 'date_format:Y-m-d|nullable|max:10',
'termination_date' => 'date_format:Y-m-d|nullable|max:10',
'min_amt' => 'numeric|nullable|gte:0',
@@ -736,4 +736,17 @@ class License extends Depreciable
return $query->leftJoin('companies as companies', 'licenses.company_id', '=', 'companies.id')->select('licenses.*')
->orderBy('companies.name', $order);
}
/**
* Query builder scope to order on the user that created it
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderCreatedBy($query, $order)
{
return $query->leftJoin('users as users_sort', 'licenses.user_id', '=', 'users_sort.id')->select('licenses.*')->orderBy('users_sort.first_name', $order)->orderBy('users_sort.last_name', $order);
}
}
+5 -4
View File
@@ -108,10 +108,11 @@ class Location extends SnipeModel
{
return Gate::allows('delete', $this)
&& ($this->assets_count === 0)
&& ($this->assigned_assets_count === 0)
&& ($this->children_count === 0)
&& ($this->users_count === 0);
&& ($this->assets_count == 0)
&& ($this->assigned_assets_count == 0)
&& ($this->children_count == 0)
&& ($this->accessories_count == 0)
&& ($this->users_count == 0);
}
/**
+2 -2
View File
@@ -22,9 +22,9 @@ class Manufacturer extends SnipeModel
// Declare the rules for the form validation
protected $rules = [
'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL',
'url' => 'url|nullable',
'url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
'support_email' => 'email|nullable',
'support_url' => 'nullable|url',
'support_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
'warranty_lookup_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://'
];
+1
View File
@@ -9,6 +9,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use App\Helpers\Helper;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
use Illuminate\Support\Facades\Log;
+1 -1
View File
@@ -331,7 +331,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
*/
public function accessories()
{
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_users', 'assigned_to', 'accessory_id')
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_checkout', 'assigned_to', 'accessory_id')
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
}
@@ -24,6 +24,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
$this->item_tag = $params['item_tag'];
$this->item_model = $params['item_model'];
$this->item_serial = $params['item_serial'];
$this->item_status = $params['item_status'];
$this->accepted_date = Helper::getFormattedDateObject($params['accepted_date'], 'date', false);
$this->assigned_to = $params['assigned_to'];
$this->note = $params['note'];
@@ -65,6 +66,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
'item_tag' => $this->item_tag,
'item_model' => $this->item_model,
'item_serial' => $this->item_serial,
'item_status' => $this->item_status,
'note' => $this->note,
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,
@@ -24,6 +24,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
$this->item_tag = $params['item_tag'];
$this->item_model = $params['item_model'];
$this->item_serial = $params['item_serial'];
$this->item_status = $params['item_status'];
$this->declined_date = Helper::getFormattedDateObject($params['declined_date'], 'date', false);
$this->note = $params['note'];
$this->assigned_to = $params['assigned_to'];
@@ -63,6 +64,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
'item_tag' => $this->item_tag,
'item_model' => $this->item_model,
'item_serial' => $this->item_serial,
'item_status' => $this->item_status,
'note' => $this->note,
'declined_date' => $this->declined_date,
'assigned_to' => $this->assigned_to,
@@ -162,6 +162,7 @@ class CheckinAssetNotification extends Notification
$message = (new MailMessage)->markdown('notifications.markdown.checkin-asset',
[
'item' => $this->item,
'status' => $this->item->assetstatus?->name,
'admin' => $this->admin,
'note' => $this->note,
'target' => $this->target,
@@ -30,6 +30,7 @@ class CheckoutAccessoryNotification extends Notification
$this->item = $accessory;
$this->admin = $checkedOutBy;
$this->note = $note;
$this->checkout_qty = $accessory->checkout_qty;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
@@ -107,7 +108,7 @@ class CheckoutAccessoryNotification extends Notification
->from($botname)
->to($channel)
->attachment(function ($attachment) use ($item, $note, $admin, $fields) {
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
$attachment->title(htmlspecialchars_decode($this->checkout_qty.' x '.$item->present()->name), $item->present()->viewUrl())
->fields($fields)
->content($note);
});
@@ -127,6 +128,7 @@ class CheckoutAccessoryNotification extends Notification
->addStartGroupToSection('activityText')
->fact(htmlspecialchars_decode($item->present()->name), '', 'activityTitle')
->fact(trans('mail.assigned_to'), $target->present()->name)
->fact(trans('general.qty'), $this->checkout_qty)
->fact(trans('mail.checkedout_from'), $item->location->name ? $item->location->name : '')
->fact(trans('mail.Accessory_Checkout_Notification') . " by ", $admin->present()->fullName())
->fact(trans('admin/consumables/general.remaining'), $item->numRemaining())
@@ -184,6 +186,7 @@ class CheckoutAccessoryNotification extends Notification
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'checkout_qty' => $this->checkout_qty,
])
->subject(trans('mail.Confirm_accessory_delivery'));
}
@@ -209,6 +209,7 @@ public function toGoogleChat()
[
'item' => $this->item,
'admin' => $this->admin,
'status' => $this->item->assetstatus?->name,
'note' => $this->note,
'target' => $this->target,
'fields' => $fields,
+1 -1
View File
@@ -90,7 +90,7 @@ class AccessoryPresenter extends Presenter
'visible' => false,
'title' => trans('admin/accessories/general.remaining'),
],[
'field' => 'users_count',
'field' => 'checkouts_count',
'searchable' => false,
'sortable' => true,
'visible' => true,
+3 -4
View File
@@ -556,13 +556,12 @@ class AssetPresenter extends Presenter
}
/**
* Used to take user created warranty URL and dynamically fill in the needed values per asset
* Used to take user created URL and dynamically fill in the needed values per asset
* @return string
*/
public function dynamicWarrantyUrl()
public function dynamicUrl($dynamic_url)
{
$warranty_lookup_url = $this->model->model->manufacturer->warranty_lookup_url;
$url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale, $warranty_lookup_url));
$url = (str_replace('{LOCALE}',\App\Models\Setting::getSettings()->locale, $dynamic_url));
$url = (str_replace('{SERIAL}', urlencode($this->model->serial), $url));
$url = (str_replace('{MODEL_NAME}', urlencode($this->model->model->name), $url));
$url = (str_replace('{MODEL_NUMBER}', urlencode($this->model->model->model_number), $url));
+7
View File
@@ -158,6 +158,13 @@ class LicensePresenter extends Presenter
'sortable' => true,
'visible' => false,
'title' => trans('general.order_number'),
], [
'field' => 'created_by',
'searchable' => false,
'sortable' => true,
'title' => trans('general.admin'),
'visible' => false,
'formatter' => 'usersLinkObjFormatter',
], [
'field' => 'created_at',
'searchable' => false,
+11 -7
View File
@@ -445,26 +445,30 @@ class UserPresenter extends Presenter
return Storage::disk('public')->url('avatars/'.e($this->avatar));
}
// If there is a default avatar
if (Setting::getSettings()->default_avatar!= '') {
// If the default is system default
if (Setting::getSettings()->default_avatar == 'default.png') {
return Storage::disk('public')->url('default.png');
}
// If there is a custom default avatar
if (Setting::getSettings()->default_avatar != '') {
return Storage::disk('public')->url('avatars/'.e(Setting::getSettings()->default_avatar));
}
// Fall back to Gravatar if the settings allow loading remote scripts
if (Setting::getSettings()->load_remote == '1') {
if ($this->model->gravatar != '') {
// If there is no default and no custom avatar, check for gravatar
if ((Setting::getSettings()->load_remote == '1') && (Setting::getSettings()->default_avatar == '')) {
if ($this->model->gravatar != '') {
$gravatar = md5(strtolower(trim($this->model->gravatar)));
return '//gravatar.com/avatar/'.$gravatar;
} elseif ($this->email != '') {
$gravatar = md5(strtolower(trim($this->email)));
return '//gravatar.com/avatar/'.$gravatar;
}
}
return false;
}
+4 -3
View File
@@ -87,11 +87,11 @@ class AuthServiceProvider extends ServiceProvider
]);
$this->registerPolicies();
//Passport::routes(); //this is no longer required in newer passport versions
Passport::tokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
Passport::refreshTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
Passport::personalAccessTokensExpireIn(Carbon::now()->addYears(config('passport.expiration_years')));
Passport::withCookieSerialization();
Passport::cookie(config('passport.cookie_name'));
/**
@@ -230,7 +230,8 @@ class AuthServiceProvider extends ServiceProvider
|| $user->can('update', Accessory::class)
|| $user->can('create', Accessory::class)
|| $user->can('update', User::class)
|| $user->can('create', User::class);
|| $user->can('create', User::class)
|| ($user->hasAccess('reports.view'));
});
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class BladeServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot(): void
{
Blade::anonymousComponentPath(__DIR__ . '/../../resources/views/blade');
}
}
+1 -2
View File
@@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rule;
use Validator;
use Illuminate\Support\Facades\Validator;
/**
* This service provider handles a few custom validation rules.
@@ -66,7 +66,6 @@ class ValidationServiceProvider extends ServiceProvider
* `unique_undeleted:table,fieldname` in your rules out of the box
*/
Validator::extend('unique_undeleted', function ($attribute, $value, $parameters, $validator) {
if (count($parameters)) {
// This is a bit of a shim, but serial doesn't have any other rules around it other than that it's nullable
+3 -3
View File
@@ -337,12 +337,12 @@ class Saml
/**
* Get a setting.
*
* @author Johnson Yi <jyi.dev@outlook.com>
*
* @param string|array|int $key
* @param mixed $default
*
* @return void
* @return mixed
* @author Johnson Yi <jyi.dev@outlook.com>
*
*/
public function getSetting($key, $default = null)
{
+1 -1
View File
@@ -107,7 +107,7 @@ class Label implements View
if ($settings->alt_barcode_enabled) {
if ($template->getSupport1DBarcode()) {
$barcode1DType = $settings->alt_barcode;
$barcode1DType = $settings->label2_1d_type;
if ($barcode1DType != 'none') {
$assetData->put('barcode1d', (object)[
'type' => $barcode1DType,
+4 -4
View File
@@ -52,7 +52,7 @@
"livewire/livewire": "^3.5",
"neitanod/forceutf8": "^2.0",
"nesbot/carbon": "^2.32",
"nunomaduro/collision": "^6.1",
"nunomaduro/collision": "^7.0",
"okvpn/clock-lts": "^1.0",
"onelogin/php-saml": "^3.4",
"paragonie/constant_time_encoding": "^2.3",
@@ -74,13 +74,13 @@
"ext-exif": "*"
},
"require-dev": {
"brianium/paratest": "^v6.4.4",
"brianium/paratest": "^7.0",
"fakerphp/faker": "^1.16",
"larastan/larastan": "^2.9",
"mockery/mockery": "^1.4",
"nunomaduro/phpinsights": "^2.7",
"nunomaduro/phpinsights": "^2.11",
"php-mock/php-mock-phpunit": "^2.10",
"phpunit/phpunit": "^9.6.19",
"phpunit/phpunit": "^10.0",
"squizlabs/php_codesniffer": "^3.5",
"symfony/css-selector": "^4.4",
"symfony/dom-crawler": "^4.4",
Generated
+591 -627
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -313,6 +313,7 @@ return [
/*
* Custom Service Providers...
*/
App\Providers\BladeServiceProvider::class,
App\Providers\LivewireServiceProvider::class,
App\Providers\MacroServiceProvider::class,
App\Providers\SamlServiceProvider::class,
@@ -427,5 +428,5 @@ return [
*/
'escape_formulas' => env('CSV_ESCAPE_FORMULAS', true),
];
+1 -1
View File
@@ -139,7 +139,7 @@ return [
'to' => env('MAIL_BACKUP_NOTIFICATION_ADDRESS', null),
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'address' => env('MAIL_FROM_ADDR', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
],
+1
View File
@@ -14,4 +14,5 @@ return [
'private_key' => env('PASSPORT_PRIVATE_KEY'),
'public_key' => env('PASSPORT_PUBLIC_KEY'),
'expiration_years' => env('API_TOKEN_EXPIRATION_YEARS', 20),
'cookie_name' => env('PASSPORT_COOKIE_NAME', 'snipeit_passport_token'),
];
-6
View File
@@ -44,12 +44,6 @@ return [
'secret' => env('STRIPE_SECRET'),
],
'baremetrics' => [
'enabled' => env('ENABLE_BMPAY', false),
'app_key' => env('BMPAY_PUBLIC_KEY', null),
'stripe_id' => env('BMPAY_STRIPE_ID', null),
],
'google' => [
'maps_api_key' => env('GOOGLE_MAPS_API'),
],
+6 -6
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v7.0.9',
'full_app_version' => 'v7.0.9 - build 14371-g1a541ce22',
'build_version' => '14371',
'app_version' => 'v7.0.11',
'full_app_version' => 'v7.0.11 - build 14904-g6c0cf9447',
'build_version' => '14904',
'prerelease_version' => '',
'hash_version' => 'g1a541ce22',
'full_hash' => 'v7.0.9-119-g1a541ce22',
'branch' => 'develop',
'hash_version' => 'g6c0cf9447',
'full_hash' => 'v7.0.11-218-g6c0cf9447',
'branch' => 'master',
);
+6 -3
View File
@@ -3,6 +3,7 @@
namespace Database\Factories;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Category;
use App\Models\Location;
use App\Models\Manufacturer;
@@ -125,11 +126,12 @@ class AccessoryFactory extends Factory
})->afterCreating(function ($accessory) {
$user = User::factory()->create();
$accessory->users()->attach($accessory->id, [
$accessory->checkouts()->create([
'accessory_id' => $accessory->id,
'created_at' => now(),
'created_at' => Carbon::now(),
'user_id' => $user->id,
'assigned_to' => $user->id,
'assigned_type' => User::class,
'note' => '',
]);
});
@@ -145,11 +147,12 @@ class AccessoryFactory extends Factory
public function checkedOutToUser(User $user = null)
{
return $this->afterCreating(function (Accessory $accessory) use ($user) {
$accessory->users()->attach($accessory->id, [
$accessory->checkouts()->create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => 1,
'assigned_to' => $user->id ?? User::factory()->create()->id,
'assigned_type' => User::class,
]);
});
}
+12 -1
View File
@@ -48,6 +48,7 @@ class AssetFactory extends Factory
'assigned_type' => null,
'next_audit_date' => null,
'last_checkout' => null,
'asset_eol_date' => null
];
}
@@ -354,6 +355,17 @@ class AssetFactory extends Factory
return $this->state(['requestable' => false]);
}
public function noPurchaseOrEolDate()
{
return $this->afterCreating(function (Asset $asset) {
$asset->update([
'purchase_date' => null,
'asset_eol_date' => null
]);
});
}
public function hasEncryptedCustomField(CustomField $field = null)
{
return $this->state(function () use ($field) {
@@ -372,7 +384,6 @@ class AssetFactory extends Factory
});
}
/**
* This allows bypassing model level validation if you want to purposefully
* create an asset in an invalid state. Validation is turned back on
+16
View File
@@ -2,10 +2,15 @@
namespace Database\Factories;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Category;
use App\Models\Company;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\Location;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Supplier;
@@ -97,5 +102,16 @@ class ComponentFactory extends Factory
});
}
public function checkedOutToAsset(Asset $asset = null)
{
return $this->afterCreating(function (Component $component) use ($asset) {
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'created_at' => Carbon::now(),
'user_id' => 1,
'asset_id' => $asset->id ?? Asset::factory()->create()->id,
]);
});
}
}
+7 -1
View File
@@ -25,7 +25,13 @@ class LocationFactory extends Factory
'image' => rand(1, 9).'.jpg',
];
}
// one of these can eventuall go away - left temporarily for conflict resolution
public function deleted(): self
{
return $this->state(['deleted_at' => $this->faker->dateTime()]);
}
public function deletedLocation()
{
return $this->state(function () {
+5
View File
@@ -309,4 +309,9 @@ class UserFactory extends Factory
];
});
}
public function deleted(): self
{
return $this->state(['deleted_at' => $this->faker->dateTime()]);
}
}

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