Compare commits

..

221 Commits

Author SHA1 Message Date
snipe 4e6764428e Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2023-10-02 14:29:59 +01:00
snipe c7b8880d69 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2023-10-02 14:28:59 +01:00
snipe 228f21ed91 Merge pull request #13690 from snipe/localization/all
Updated language strings
2023-10-02 14:24:52 +01:00
snipe b7c390a257 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2023-10-02 14:23:22 +01:00
snipe c45ede2d17 Merge remote-tracking branch 'origin/develop' 2023-09-29 19:00:27 +01:00
snipe 758d808772 Merge pull request #13683 from snipe/bug/sc-17602
Fixed consumable user view route name
2023-09-29 15:17:23 +01:00
snipe 754ec7563a Fixed consumable user view route name
Signed-off-by: snipe <snipe@snipe.net>
2023-09-29 15:15:03 +01:00
snipe 352e53a036 Merge remote-tracking branch 'origin/develop' 2023-09-29 01:51:40 +01:00
snipe 0184d25a95 Merge pull request #13681 from marcusmoore/bug/sc-23778
Fixed uncaught type error when attempting to render barcodes with invalid characters
2023-09-29 01:50:07 +01:00
Marcus Moore bed1055c4e Catch TypeError when rendering 1d barcode 2023-09-28 17:32:16 -07:00
Marcus Moore 41eccaeae0 Set dummy asset tag to value that is valid for EAN13 2023-09-28 17:31:45 -07:00
snipe c1f545a523 Merge pull request #13679 from Godmartinz/bug/sc-23688
Fixed General Webhooks error messaging and validation
2023-09-28 20:24:09 +01:00
Godfrey M 8792d654b6 added translations for messages 2023-09-28 12:15:17 -07:00
Godfrey M f8730adb11 removed unnecessary changes 2023-09-28 11:41:43 -07:00
Godfrey M c21586dee5 adds better error messaging 2023-09-28 11:29:46 -07:00
snipe 581e56198c Merge remote-tracking branch 'origin/develop' 2023-09-28 19:24:48 +01:00
snipe f16e81e0e8 Merge pull request #13678 from spencerrlongg/bug/sc-23596
Fieldset Properly Sortable in Asset Models Table
2023-09-28 19:24:04 +01:00
snipe 3de656f5c6 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
2023-09-28 19:20:27 +01:00
snipe 3b4004ac18 Merge pull request #13677 from snipe/feature/sc-14880
Set modal focus to modal-name field
2023-09-28 19:18:16 +01:00
snipe 0bf423cda9 Set modal focus to modal-name field
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 19:16:40 +01:00
spencerrlongg a54fab5c33 add fieldset to allowed_columns 2023-09-28 13:02:12 -05:00
snipe 96c6c93197 Merge pull request #13674 from snipe/bug/sc-23774
Fixed bug where license checkout/checkin notes were not being saved
2023-09-28 18:37:47 +01:00
spencerrlongg 4d67c72eea scope and switch case in api 2023-09-28 12:26:23 -05:00
snipe 6fa0d42bc2 Fixed bug where license checkout/checkin notes were not being saved
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 15:40:18 +01:00
snipe cfe6b07b3a Updated prod assets
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/all.css
#	public/css/dist/bootstrap-table.css
#	public/js/build/vendor.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2023-09-28 15:15:50 +01:00
snipe 7512400b56 Merge pull request #13672 from snipe/feature/sc-23772
Fixed #13662 - added clipboard.js
2023-09-28 15:13:42 +01:00
snipe 1acb14a39f Moved dragtable css
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 15:12:44 +01:00
snipe bb7a41628a Fixed #13662 - added clipboard.js
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 15:06:55 +01:00
snipe 68c9fac971 Merge remote-tracking branch 'origin/develop' 2023-09-28 14:25:31 +01:00
snipe 402ff58628 Merge pull request #13671 from snipe/bug/sc-23773
Fixed #13670 - order number missing from license import
2023-09-28 14:24:49 +01:00
snipe 492fb15036 Fixed #13670 - order number missing from license import
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 14:24:07 +01:00
snipe ac7f85fea9 Merge remote-tracking branch 'origin/develop' 2023-09-28 03:32:57 +01:00
snipe cc4cb14e9d Merge pull request #13667 from snipe/bug/sc-23771
Fixed #13658 for asset history with encrypted fields
2023-09-28 03:29:06 +01:00
snipe ee72c92d4f Fix for asset history with enfrypted fields
Signed-off-by: snipe <snipe@snipe.net>
2023-09-28 03:00:49 +01:00
snipe 3aeea007b2 Merge remote-tracking branch 'origin/develop' 2023-09-26 15:26:21 +01:00
snipe 048ad57418 Merge pull request #13657 from snipe/fixes/revert_column_sorting_temp
Commented out sortable columns
2023-09-26 15:25:58 +01:00
snipe 7bcb28d8fd Commented out sortable columns
Signed-off-by: snipe <snipe@snipe.net>
2023-09-26 15:24:20 +01:00
snipe 660abeca9e Merge pull request #13638 from marcusmoore/bug/sc-20704
Fixed exception being thrown when selected import was deleted
2023-09-26 15:00:35 +01:00
snipe 0c31d5749c Merge remote-tracking branch 'origin/develop' 2023-09-26 14:47:25 +01:00
snipe 1509339b68 Merge pull request #13655 from snipe/bug/sc-23759
Fixed bug where checkout to location would throw an error if FMCS was enabled
2023-09-26 14:37:36 +01:00
snipe 81ae32d3f9 Fixed typo
Signed-off-by: snipe <snipe@snipe.net>
2023-09-26 14:33:51 +01:00
snipe 279e6c7e4f Check for null company ID
Signed-off-by: snipe <snipe@snipe.net>
2023-09-26 14:33:42 +01:00
snipe 16498fdcf8 Merge remote-tracking branch 'origin/develop' 2023-09-25 13:46:33 +01:00
snipe 81a982fd77 Merge pull request #13650 from snipe/features/more_info_in_settings
Features/more info in settings
2023-09-25 13:46:00 +01:00
snipe 619712b927 Fixed table
Signed-off-by: snipe <snipe@snipe.net>
2023-09-25 13:45:46 +01:00
snipe 359cd2a267 A few more config options
Signed-off-by: snipe <snipe@snipe.net>
2023-09-25 13:28:13 +01:00
snipe 13bee63fe9 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2023-09-25 10:59:09 +01:00
snipe f6317695f9 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2023-09-25 10:58:14 +01:00
snipe e4696138ba Merge pull request #13647 from snipe/localization/new_translations
Localization/new translations
2023-09-25 10:57:02 +01:00
snipe bd0863d9c7 Added new translations
Signed-off-by: snipe <snipe@snipe.net>
2023-09-25 10:54:46 +01:00
snipe 28cd0085b7 Added Khmer as an available language
Signed-off-by: snipe <snipe@snipe.net>
2023-09-25 10:54:32 +01:00
snipe 456d55c3bd Added some mail settings to /settings for easier reference
Signed-off-by: snipe <snipe@snipe.net>
2023-09-21 16:14:47 +01:00
Marcus Moore 5a88a64ebd Remove unnecessary redirect 2023-09-20 17:35:34 -07:00
Marcus Moore b1199100a0 Display error message if import file deleted before it can be selected 2023-09-20 17:22:12 -07:00
Godfrey M b7901ae2d8 dont allow redirects, messaging applied, general webhook validation applied 2023-09-20 12:54:34 -07:00
snipe 7c908fecd9 Merge pull request #12992 from spencerrlongg/features/12853
Adds tag for docker builds
2023-09-20 19:36:22 +01:00
snipe cdd0de15b5 Merge pull request #13634 from marcusmoore/chore/add-factories-to-autolabeler
Added factories to autolabeler action
2023-09-20 17:51:34 +01:00
snipe 46981da400 Merge pull request #13635 from marcusmoore/bug/sc-23715
Added unique() to some factory properties
2023-09-20 17:51:22 +01:00
Godfrey M 66abf8d5c0 redoing logic 2023-09-19 17:46:29 -07:00
Marcus Moore 90e75a3e2c Add unique() to some factory properties 2023-09-19 17:02:18 -07:00
Marcus Moore cd538a4ad8 Add factories to autolabeler action 2023-09-19 16:07:26 -07:00
snipe 5a5b07f5a5 Merge remote-tracking branch 'origin/develop' 2023-09-19 10:22:59 +01:00
snipe 439c2e095d Merge pull request #13629 from snipe/bug/sc-23745
Fixed #13628 - removed duplicate favicon tag
2023-09-19 10:22:36 +01:00
snipe 14c0517927 Fixed #13628 - removed duplicate favicon tag
Signed-off-by: snipe <snipe@snipe.net>
2023-09-19 10:21:38 +01:00
snipe d84d26febf Merge remote-tracking branch 'origin/develop' 2023-09-18 22:31:41 +01:00
snipe 753a5f789e Merge pull request #13623 from snipe/fixes/dependabot_target
Made develop the target branch for dependabot
2023-09-18 22:31:11 +01:00
snipe d7790cd16b Updated assets
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:24:15 +01:00
snipe 8ecc0651ed 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
2023-09-18 22:23:58 +01:00
snipe a8b2089275 Merge pull request #13627 from snipe/fixes/dependabot_13616
Replaces #13616 - Bump docker/metadata-action from 4 to 5
2023-09-18 22:20:06 +01:00
snipe a46a11d554 Replaces #13616 - Bump docker/metadata-action from 4 to 5
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:19:31 +01:00
snipe d444b620c5 Merge pull request #13626 from snipe/fixes/dependabot_13617
Replaces #13617 - Bump docker/setup-buildx-action from 2 to 3
2023-09-18 22:18:04 +01:00
snipe 10dcd8166b Replaces #13617 - Bump docker/setup-buildx-action from 2 to 3
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:17:24 +01:00
snipe 00df449e0b Merge pull request #13625 from snipe/fixes/dependabot_13618
Replaces #13618 - Bump docker/build-push-action from 4 to 5
2023-09-18 22:16:08 +01:00
snipe 0cb4427a19 Bump docker/build-push-action from 4 to 5
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:15:18 +01:00
snipe 17fe915d2f Merge pull request #13624 from snipe/fixes/dependabot_13619
Bump docker/login-action from 2 to 3 (#13619)
2023-09-18 22:14:01 +01:00
snipe 9ad73ed09a Bump docker/login-action from 2 to 3 (#13619)
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:13:09 +01:00
snipe 68fa6d57ca Merge pull request #13613 from snipe/features/reorder_columns
Added column re-ordering
2023-09-18 22:10:09 +01:00
snipe f14e3422b4 Made develop the target branch
Signed-off-by: snipe <snipe@snipe.net>
2023-09-18 22:09:43 +01:00
snipe 2a638e66f0 Added column re-ordering
Signed-off-by: snipe <snipe@snipe.net>
2023-09-15 21:53:34 +01:00
snipe 8bdecb1da9 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/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2023-09-15 20:32:05 +01:00
snipe 52f9c786be Merge pull request #13612 from snipe/fixes/re_add_sticky_header
Fixed regression that disabled sticky headers
2023-09-15 20:29:24 +01:00
snipe 3ba8b60f54 Fixed regression that disabled sticky headers
Signed-off-by: snipe <snipe@snipe.net>
2023-09-15 20:28:28 +01:00
snipe 341f711385 Merge remote-tracking branch 'origin/develop' 2023-09-15 15:43:48 +01:00
snipe eb406547c4 Merge pull request #13610 from snipe/fixes/disallow_sorting_on_age
Fixes #13609 - disallow sorting on age
2023-09-15 15:26:55 +01:00
snipe 0b562d2f55 Fixes #13609 - disallow sorting on age
Signed-off-by: snipe <snipe@snipe.net>
2023-09-15 15:25:52 +01:00
snipe 131ccaa5b9 Merge pull request #12869 from Godmartinz/bug/sc-23134
Fixed footer sizing issue
2023-09-15 14:31:07 +01:00
snipe 9bd357e2d4 Merge remote-tracking branch 'origin/develop' 2023-09-15 12:52:59 +01:00
snipe 054d8763fb Merge pull request #13608 from snipe/bug/sc-15287
Use relative path in backups for cleaner directory structure
2023-09-15 12:51:49 +01:00
snipe 52a77f4c31 Use relative path in backups for cleaner directory structure
Signed-off-by: snipe <snipe@snipe.net>
2023-09-15 12:50:00 +01:00
snipe cce90c6ce0 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2023-09-15 09:43:25 +01:00
snipe 4e0e1a2c85 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2023-09-15 09:42:27 +01:00
snipe 8a8afef81b Merge pull request #13606 from snipe/feature/sc-23705
Fixed #13592 - unable to delete model from view page
2023-09-15 01:11:49 +01:00
snipe 860e7791ff Fixed #13592 - unable to delete model from view page
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 23:35:44 +01:00
snipe 20da6cccbc Merge remote-tracking branch 'origin/develop' 2023-09-14 22:57:04 +01:00
snipe a013ddf541 Merge pull request #13605 from snipe/bug/sc-23183
Simplify upload messaging, handle -1% error
2023-09-14 22:52:31 +01:00
snipe bbe7d9dde2 Removed stray space
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 22:08:53 +01:00
snipe 932c02cc54 Typo
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 22:08:30 +01:00
snipe 84b0ff76ed Revert large warning size
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 22:06:19 +01:00
snipe 273740b8f5 Fixed import error warning
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 22:04:23 +01:00
snipe 34c3174531 Merge pull request #13604 from marcusmoore/feature/sc-23714
Removed ChipperCI integration
2023-09-14 21:48:49 +01:00
snipe 8f4ca9da57 Simplify upload messaging
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 21:20:21 +01:00
Marcus Moore b113411891 Remove ChipperCI 2023-09-14 13:00:38 -07:00
snipe 15c71439b6 Merge pull request #13602 from snipe/bug/sc-23685
Fixed checkbox formatting on quickstart
2023-09-14 18:48:50 +01:00
snipe 8e41fb7b36 Removed stray text
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 18:48:20 +01:00
snipe d0c4910a51 Fixed checkbox formatting on quickstart
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 18:44:02 +01:00
snipe 467609e561 Merge remote-tracking branch 'origin/develop' 2023-09-14 14:32:06 +01:00
snipe 2166d6649d Merge pull request #13343 from snipe/fixes/re-scramble_password_if_ldap_pw_sync_not_enabled
Fixed #13336 - Save unhashed password if no password provided
2023-09-14 14:15:53 +01:00
snipe dcae5503c8 Use $attributes array
Signed-off-by: snipe <snipe@snipe.net>
2023-09-14 13:52:57 +01:00
snipe d916e20c10 Merge pull request #13415 from marcusmoore/feature/department-scoping
Added `CompanyableTrait` to `Department` model
2023-09-14 12:52:53 +01:00
snipe b92327eb40 Merge pull request #13520 from inietov/fixes/licenses_reassignable_feature
Fixed Not reassignable Licenses shouldn't show 'Checkin All Seats' button [sc-23506]
2023-09-14 12:52:13 +01:00
snipe c7b24821b3 Merge pull request #13549 from inietov/fixes/attempt_to_read_property_id_on_null
Fixed ErrorException: Attempt to read property "id" on null (rollbar #3541)
2023-09-14 12:50:54 +01:00
snipe a37df8ce9d Merge pull request #13581 from snipe/updates/upgrade_checkout_actions
Upgraded actions/checkout to v4 (via #13580)
2023-09-14 12:40:34 +01:00
snipe 104219ed76 Merge pull request #13597 from marcusmoore/bug/sc-23681
Reset checkout to value when navigating away from type
2023-09-14 12:39:04 +01:00
snipe 56c7da7b06 Merge pull request #13595 from Godmartinz/correct-nullable_on_asset_model_min_qty
Fixed nullability on asset model `min qty` column
2023-09-14 08:52:33 +01:00
Ivan Nieto Vivanco 439e031911 Evaluate if the event properties exists before run the CheckoutAcceptance query 2023-09-13 16:35:10 -06:00
Marcus Moore 74c7f106ce Reset assigned_x values when changing check out to type 2023-09-13 13:43:35 -07:00
Godfrey M f283a5f755 changed the down method change 2023-09-13 13:24:26 -07:00
Godfrey M 0c504fed49 fixed nullability of on Asset Models 2023-09-13 13:13:41 -07:00
Marcus Moore 11208ee064 Guard against attempting to send notification to model that isn't notifiable 2023-09-13 12:56:27 -07:00
snipe c2fe9d490b Upgraded actions/checkout to v4 (via #13580)
Signed-off-by: snipe <snipe@snipe.net>
2023-09-11 13:25:44 +01:00
snipe 9c608dd6ff Merge remote-tracking branch 'origin/develop' 2023-09-07 21:36:39 +01:00
snipe 5b88089ffc Merge remote-tracking branch 'origin/develop' 2023-09-07 20:50:35 +01:00
snipe cf36c31eac Merge remote-tracking branch 'origin/develop' 2023-09-07 20:22:33 +01:00
snipe 2a94fd17ee Merge remote-tracking branch 'origin/develop' 2023-09-06 09:37:35 +01:00
snipe 32407b531b Merge remote-tracking branch 'origin/develop' 2023-09-05 16:10:30 +01:00
snipe 521fcd45b0 Merge remote-tracking branch 'origin/develop' 2023-09-05 16:08:17 +01:00
snipe faa865fd48 Merge remote-tracking branch 'origin/develop' 2023-08-31 18:24:29 +01:00
snipe 2a1aa53ba6 Merge remote-tracking branch 'origin/develop' 2023-08-31 13:04:56 +01:00
snipe 821c3085f0 Merge remote-tracking branch 'origin/develop' 2023-08-31 12:01:58 +01:00
Ivan Nieto Vivanco 3cf9c1fea5 Adds a null coalescing operator to the license seat checkin notification 2023-08-30 11:57:29 -06:00
snipe 587a787b5d Fixed bug introduced in #13528
Signed-off-by: snipe <snipe@snipe.net>
2023-08-30 15:50:28 +01:00
snipe 305804f260 Updated assets
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	package-lock.json
#	public/css/dist/all.css
#	public/js/dist/all-defer.js
#	public/mix-manifest.json
2023-08-30 15:50:09 +01:00
snipe 319cb2305d Merge remote-tracking branch 'origin/develop' 2023-08-30 15:05:46 +01:00
snipe 18ff810d7e Reverse orderof parent
Signed-off-by: snipe <snipe@snipe.net>
2023-08-28 20:51:52 +01:00
snipe baffcbad71 Set password property properly
Signed-off-by: snipe <snipe@snipe.net>
2023-08-28 20:47:56 +01:00
snipe 611db4c0d2 Removed stray line
Signed-off-by: snipe <snipe@snipe.net>
2023-08-28 20:46:45 +01:00
snipe 0f43388a2b Merge remote-tracking branch 'origin/develop' 2023-08-28 20:40:12 +01:00
snipe b93adf44c8 Merge remote-tracking branch 'origin/develop' 2023-08-28 12:41:45 +01:00
snipe a1897298dc Merge remote-tracking branch 'origin/develop' 2023-08-27 13:56:54 +01:00
snipe 0a47706e46 Merge remote-tracking branch 'origin/develop' 2023-08-25 15:47:47 +01:00
Ivan Nieto Vivanco 7bfb5a0667 Disable the Checkin All Seats button if License is not reassignable 2023-08-23 16:36:06 -06:00
Ivan Nieto Vivanco 6161a0d76d Add condition in LicenseCheckinController:bulkCheckin method to evaluate if the license is reassignable 2023-08-23 16:22:40 -06:00
snipe 9f291d7e4b Merge remote-tracking branch 'origin/develop' 2023-08-23 08:21:53 +01:00
snipe bb61134dd5 Merge remote-tracking branch 'origin/develop' 2023-08-22 12:41:27 +01:00
snipe 24a9deb735 Merge remote-tracking branch 'origin/develop' 2023-08-21 22:35:45 +01:00
snipe cb3db51fe0 Merge remote-tracking branch 'origin/develop' 2023-08-18 18:42:11 +01:00
snipe 8e9fa613e3 Merge remote-tracking branch 'origin/develop' 2023-08-18 18:14:40 +01:00
snipe 384428148b Merge remote-tracking branch 'origin/develop' 2023-08-18 15:32:33 +01:00
snipe 8810059427 Merge remote-tracking branch 'origin/develop' 2023-08-16 01:40:06 +01:00
snipe 2848465dd2 Merge remote-tracking branch 'origin/develop' 2023-08-15 20:42:36 +01:00
snipe 06ed8b2f2d Merge remote-tracking branch 'origin/develop' 2023-08-15 20:31:00 +01:00
snipe 2ddf5c9a0b Merge pull request #13463 from uberbrady/fix_api_accessory_checkin
Accessory checkin via API reported wrong target user
2023-08-15 19:24:45 +01:00
snipe 949a88e560 Merge remote-tracking branch 'origin/develop' 2023-08-15 18:55:39 +01:00
snipe 28b1461cb4 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/mix-manifest.json
2023-08-15 18:21:51 +01:00
Brady Wetherington 7504c0df13 Accessory checkin via API reported wrong target user 2023-08-15 14:19:36 +01:00
snipe 1c02a7d590 Merge remote-tracking branch 'origin/develop' 2023-08-15 13:26:40 +01:00
snipe 8e0b7bee41 Merge remote-tracking branch 'origin/develop' 2023-08-15 13:23:23 +01:00
snipe 642a09f139 Merge remote-tracking branch 'origin/develop' 2023-08-14 23:12:07 +01:00
snipe 716e13e5b1 Merge remote-tracking branch 'origin/develop' 2023-08-14 22:23:06 +01:00
snipe ec2556f991 Merge remote-tracking branch 'origin/develop' 2023-08-14 13:02:45 +01:00
snipe 6134dfa8f2 Merge remote-tracking branch 'origin/develop' 2023-08-11 09:42:30 +01:00
snipe 7df3be3a54 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
2023-08-09 10:54:49 +01:00
snipe 70903f068c Merge remote-tracking branch 'origin/develop' 2023-08-08 19:01:40 +01:00
snipe 5a43c5906f Merge remote-tracking branch 'origin/develop' 2023-08-08 17:54:17 +01:00
snipe 4b2bf057c7 Merge remote-tracking branch 'origin/develop' 2023-08-08 12:57:27 +01:00
snipe 3091d2cdf0 Merge remote-tracking branch 'origin/develop' 2023-08-08 08:19:15 +01:00
Marcus Moore 42055bb69d Add authentication and authorization tests for department index method 2023-08-07 17:42:28 -07:00
Marcus Moore 7c5a1b376e Remove redundant Company::scopeCompanyables wrapper 2023-08-07 16:54:23 -07:00
Marcus Moore f7b2075e9e Add CompanyableTrait to Department 2023-08-07 16:54:02 -07:00
Marcus Moore cdfe8e459d Add simple test cases for api department index 2023-08-07 16:36:01 -07:00
snipe c52a1f94dc Merge remote-tracking branch 'origin/develop' 2023-08-02 15:19:23 +01:00
snipe ff3bdebb9a Merge remote-tracking branch 'origin/develop' 2023-07-24 13:27:35 +01:00
snipe f699d9680b Merge remote-tracking branch 'origin/develop' 2023-07-21 15:00:23 +01:00
snipe d26bc19e3f Merge remote-tracking branch 'origin/develop' 2023-07-20 16:34:16 +01:00
snipe 19df0b82b1 Merge remote-tracking branch 'origin/develop' 2023-07-20 14:06:45 +01:00
snipe b54e7dc3ee Fixed #13336 - Save unhashed password if no password provided
Signed-off-by: snipe <snipe@snipe.net>
2023-07-19 17:44:40 +01:00
snipe 74a5bcd490 Merge remote-tracking branch 'origin/develop' 2023-07-19 09:01:19 +01:00
snipe bc91181917 Merge remote-tracking branch 'origin/develop' 2023-07-18 13:27:05 +01:00
snipe 92e7e79faf Merge remote-tracking branch 'origin/develop' 2023-07-18 13:25:34 +01:00
snipe 1fa703387a Merge remote-tracking branch 'origin/develop' 2023-07-18 11:30:53 +01:00
snipe 3203f8f97c Merge remote-tracking branch 'origin/develop' 2023-07-15 10:48:09 +01:00
snipe 8bc8ecfc67 Merge remote-tracking branch 'origin/develop' 2023-07-14 09:39:26 +01:00
snipe db8c37cd5b Merge remote-tracking branch 'origin/develop' 2023-07-14 08:26:12 +01:00
snipe 43d419f051 Merge remote-tracking branch 'origin/develop' 2023-07-13 13:18:52 +01:00
snipe 66875ff0dc Merge remote-tracking branch 'origin/develop' 2023-07-13 12:59:30 +01:00
snipe 8c74e906ef Merge remote-tracking branch 'origin/develop' 2023-07-12 19:22:49 +01:00
snipe 3a6d8ef684 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
2023-07-12 19:22:34 +01:00
snipe a56b040143 Merge remote-tracking branch 'origin/develop' 2023-07-05 17:30:19 +01:00
snipe 61fd427678 Bumped version
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2023-07-05 14:41:34 +01:00
snipe 32747cafde Merge remote-tracking branch 'origin/develop' 2023-07-05 14:37:53 +01:00
snipe e18c3e08be Merge remote-tracking branch 'origin/develop' 2023-06-29 21:23:53 +01:00
snipe 33b1a31ed3 Merge remote-tracking branch 'origin/develop' 2023-06-29 21:19:25 +01:00
snipe 1b71ab6d86 Updated prod assets
Signed-off-by: snipe <snipe@snipe.net>
2023-06-29 20:41:16 +01:00
snipe af26ec471f Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/mix-manifest.json
2023-06-29 20:41:05 +01:00
snipe 4dd71e0332 Merge remote-tracking branch 'origin/develop' 2023-06-29 17:14:15 +01:00
snipe 4908082240 Merge remote-tracking branch 'origin/develop' 2023-06-29 16:23:35 +01:00
snipe ead5f039b4 Merge remote-tracking branch 'origin/develop' 2023-06-29 15:33:12 +01:00
snipe 2c23c71823 Merge remote-tracking branch 'origin/develop' 2023-06-28 16:41:49 +01:00
snipe 4cb9c25e83 Merge remote-tracking branch 'origin/develop' 2023-06-26 08:30:35 +01:00
snipe c377e0617c Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2023-06-21 19:07:07 +01:00
snipe 2ac4449ea3 Merge remote-tracking branch 'origin/develop' 2023-06-15 20:30:07 +01:00
snipe 83708e1be9 Merge remote-tracking branch 'origin/develop' 2023-06-13 18:36:22 +01:00
snipe f552bcef78 Merge pull request #13143 from qay21/patch-1
Fixed #13129: Add missing LDAP lib required for LDAPS support
2023-06-12 14:04:05 +01:00
Quentin Aymard fc6c5eadd7 Fix missing ldap packages
This should provide LDAPS support out of the box, and fix #13129
2023-06-09 11:14:41 +02:00
snipe 924d0b25e8 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	public/css/dist/all.css
#	public/js/build/app.js
#	public/js/build/vendor.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2023-06-08 08:53:22 +01:00
snipe fc5b02e392 Merge remote-tracking branch 'origin/develop' 2023-06-01 19:51:49 +01:00
snipe 18eda15ec1 Merge remote-tracking branch 'origin/develop' 2023-05-31 16:19:34 +01:00
snipe c05a4452bc Merge + update assets
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
2023-05-29 18:20:12 -04:00
snipe a3a64be19b 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
2023-05-24 16:06:26 -07:00
snipe f1b4bba3ae Merge remote-tracking branch 'origin/develop' 2023-05-16 23:12:41 -07:00
snipe dbae01f545 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/mix-manifest.json
2023-05-16 17:41:39 -07:00
snipe 5be993df8d Merge remote-tracking branch 'origin/develop' 2023-05-16 16:42:17 -07:00
snipe 37f75c5001 Merge remote-tracking branch 'origin/develop' 2023-05-16 16:29:36 -07:00
snipe b1fda46e11 Production assets
Signed-off-by: snipe <snipe@snipe.net>
2023-05-10 11:25:28 -07:00
snipe 6b7a7b8aee Merge remote-tracking branch 'origin/develop' 2023-05-10 11:23:29 -07:00
snipe 63c660f306 Merge pull request #12999 from snipe/develop
Google Oauth Recap
2023-05-10 10:01:23 -07:00
slong753 9a4ba78c19 oops, pattern 2023-05-09 23:41:21 -05:00
slong753 491f670215 I think this is what they're looking for 2023-05-09 22:52:00 -05:00
Godfrey M c9eb1410d7 swaps version and open source text in the footer 2023-04-25 11:03:05 -07:00
Godfrey M 0ab400f5bc removed deadspace and pull 2023-04-18 15:23:11 -07:00
Godfrey M f15b0d8591 fix for footer 2023-04-18 15:20:38 -07:00
1245 changed files with 33388 additions and 13465 deletions
-44
View File
@@ -1,44 +0,0 @@
version: 1
environment:
php: 8.0
node: 12
services:
- mysql: 5.7
on:
push:
branches:
- master
- develop
pull_request:
branches: .*
pipeline:
- name: Setup
cmd: |
cp -v .env.testing.example .env
cp -v .env.testing.example .env.testing
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Generate Key
cmd: |
php artisan key:generate --force
- name: Passport Keys
cmd: |
php artisan passport:keys
- name: Run Migrations
cmd: |
php artisan migrate --force
- name: PHPUnit Unit Tests
cmd: |
php artisan test --testsuite Unit
- name: PHPUnit Feature Tests
cmd: |
php artisan test --testsuite Feature
+1 -1
View File
@@ -18,5 +18,5 @@ importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views
cli / artisan: ["/app/Console/*"]
LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"]
docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"]
tests: ["/tests/*", "/stubs"]
tests: ["/tests/*", "/database/factories/*", "/stubs"]
config: .github
+1
View File
@@ -2,5 +2,6 @@ version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
schedule:
interval: "weekly"
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3.3.0
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v3.3.0
uses: actions/checkout@v4
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Crowdin push
uses: crowdin/github-action@v1
+6 -5
View File
@@ -32,6 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }},suffix=-alpine
type=ref,event=tag,suffix=-alpine
type=semver,pattern=v{{major}}-latest-alpine
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
@@ -41,17 +42,17 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v3.3.0
uses: actions/checkout@v4
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
@@ -63,7 +64,7 @@ jobs:
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
id: meta_build
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: snipe/snipe-it
tags: ${{ env.IMAGE_TAGS }}
@@ -72,7 +73,7 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push 'snipe-it' image
id: docker_build
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.alpine
+6 -5
View File
@@ -32,6 +32,7 @@ jobs:
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
type=semver,pattern=v{{major}}-latest
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
@@ -41,17 +42,17 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v3.3.0
uses: actions/checkout@v4
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
@@ -63,7 +64,7 @@ jobs:
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'snipe-it' image
id: meta_build
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: snipe/snipe-it
tags: ${{ env.IMAGE_TAGS }}
@@ -72,7 +73,7 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push 'snipe-it' image
id: docker_build
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
+1 -1
View File
@@ -1 +1 @@
v18.16.0
v12.22.1
+1 -1
View File
@@ -1,4 +1,4 @@
![Build Status](https://app.chipperci.com/projects/0e5f8979-31eb-4ee6-9abf-050b76ab0383/status/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/snipe-it/localized.svg)](https://crowdin.com/project/snipe-it) [![Docker Pulls](https://img.shields.io/docker/pulls/snipe/snipe-it.svg)](https://hub.docker.com/r/snipe/snipe-it/) [![Twitter Follow](https://img.shields.io/twitter/follow/snipeitapp.svg?style=social)](https://twitter.com/snipeitapp) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/553ce52037fc43ea99149785afcfe641)](https://www.codacy.com/app/snipe/snipe-it?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=snipe/snipe-it&amp;utm_campaign=Badge_Grade)
[![All Contributors](https://img.shields.io/badge/all_contributors-326-orange.svg?style=flat-square)](#contributors) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/yZFtShAcKk) [![huntr](https://cdn.huntr.dev/huntr_security_badge_mono.svg)](https://huntr.dev)
## Snipe-IT - Open Source Asset Management System
+1 -5
View File
@@ -180,10 +180,6 @@ class LdapSync extends Command
}
}
/* Create user account entries in Snipe-IT */
$tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20);
$pass = bcrypt($tmp_pass);
$manager_cache = [];
if($ldap_default_group != null) {
@@ -229,7 +225,7 @@ class LdapSync extends Command
} else {
// Creating a new user.
$user = new User;
$user->password = $pass;
$user->password = $user->noPassword();
$user->activated = 1; // newly created users can log in by default, unless AD's UAC is in use, or an active flag is set (below)
$item['createorupdate'] = 'created';
}
-77
View File
@@ -1,77 +0,0 @@
<?php
namespace App\Helpers;
use Illuminate\Support\Facades\Gate;
/*********************
* These two helper methods are more designed for being re-used with the new HasCustomFields Trait
*
* The 'transform' method is designed for BlahTransformer things that need to return custom field values.
*
* The 'present' method is designed for when you're trying to generate fieldlists for use in Bootstrap tables
* - typically the 'dataTableLayout' method
*
*********************/
class CustomFieldHelper {
static function transform($fieldset, $item) {
if ($fieldset && ($fieldset->fields->count() > 0)) {
$fields_array = [];
foreach ($fieldset->fields as $field) {
if ($field->isFieldDecryptable($item->{$field->db_column})) {
$decrypted = Helper::gracefulDecrypt($field, $item->{$field->db_column});
$value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted'));
if ($field->format == 'DATE'){
if (Gate::allows('assets.view.encrypted_custom_fields')){
$value = Helper::getFormattedDateObject($value, 'date', false);
} else {
$value = strtoupper(trans('admin/custom_fields/general.encrypted'));
}
}
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'field_format' => $field->format,
'element' => $field->element,
];
} else {
$value = $item->{$field->db_column};
if (($field->format == 'DATE') && (!is_null($value)) && ($value!='')){
$value = Helper::getFormattedDateObject($value, 'date', false);
}
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'field_format' => $field->format,
'element' => $field->element,
];
}
return $fields_array;
}
} else {
return new \stdClass; // HACK to force generation of empty object instead of empty list
}
}
static function present($field) {
return [
'field' => 'custom_fields.'.$field->db_column,
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => $field->name,
'formatter'=> 'customFieldsFormatter',
'escape' => true,
'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '',
'visible' => ($field->show_in_listview == '1') ? true : false,
];
}
}
-11
View File
@@ -575,17 +575,6 @@ class Helper
return $customfields;
}
/**
* Get all of the different types of custom fields there are
* TODO - how to make this more general? Or more useful? or more dynamic?
* idea - key of classname, *value* of trans? (thus having to make this a method, which is fine)
*/
static $itemtypes_having_custom_fields = [
0 => \App\Models\Asset::class,
1 => \App\Models\User::class,
// 2 => \App\Models\Accessory::class
];
/**
* Get the list of custom field formats in an array to make a dropdown menu
*
@@ -46,6 +46,7 @@ class AssetModelsController extends Controller
'requestable',
'assets_count',
'category',
'fieldset',
];
$assetmodels = AssetModel::select([
@@ -65,7 +66,7 @@ class AssetModelsController extends Controller
'models.deleted_at',
'models.updated_at',
])
->with('category', 'depreciation', 'manufacturer')
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues')
->withCount('assets as assets_count');
if ($request->input('status')=='deleted') {
@@ -94,6 +95,9 @@ class AssetModelsController extends Controller
case 'category':
$assetmodels->OrderCategory($order);
break;
case 'fieldset':
$assetmodels->OrderFieldset($order);
break;
default:
$assetmodels->orderBy($sort, $order);
break;
+51 -3
View File
@@ -110,7 +110,7 @@ class AssetsController extends Controller
$filter = json_decode($request->input('filter'), true);
}
$all_custom_fields = CustomField::where('type', Asset::class); //used as a 'cache' of custom fields throughout this page load
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
}
@@ -573,8 +573,42 @@ class AssetsController extends Controller
$asset = $request->handleImages($asset);
$asset->customFill($request, Auth::user(), true);
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
// Set the field value based on what was sent in the request
$field_val = $request->input($field->db_column, null);
// If input value is null, use custom field's default value
if ($field_val == null) {
\Log::debug('Field value for '.$field->db_column.' is null');
$field_val = $field->defaultValue($request->get('model_id'));
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
if ($field->field_encrypted == '1') {
\Log::debug('This model field is encrypted in this fieldset.');
if (Gate::allows('admin')) {
// If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id') != '')) {
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id')));
} else {
$field_val = \Crypt::encrypt($request->input($field->db_column));
}
}
}
$asset->{$field->db_column} = $field_val;
}
}
if ($asset->save()) {
if ($request->get('assigned_user')) {
@@ -634,7 +668,21 @@ class AssetsController extends Controller
$asset = $request->handleImages($asset);
$asset->customFill($request,Auth::user());
// Update custom fields
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
if ($request->has($field->db_column)) {
if ($field->field_encrypted == '1') {
if (Gate::allows('admin')) {
$asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column));
}
} else {
$asset->{$field->db_column} = $request->input($field->db_column);
}
}
}
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
@@ -35,7 +35,7 @@ class CustomFieldsetsController extends Controller
public function index()
{
$this->authorize('index', CustomField::class);
$fieldsets = CustomFieldset::withCount('fields as fields_count')->get();
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
}
@@ -125,7 +125,7 @@ class CustomFieldsetsController extends Controller
$this->authorize('delete', CustomField::class);
$fieldset = CustomFieldset::findOrFail($id);
$modelsCount = $fieldset->customizables()->count();
$modelsCount = $fieldset->models->count();
$fieldsCount = $fieldset->fields->count();
if (($modelsCount > 0) || ($fieldsCount > 0)) {
@@ -27,7 +27,7 @@ class DepartmentsController extends Controller
$this->authorize('view', Department::class);
$allowed_columns = ['id', 'name', 'image', 'users_count'];
$departments = Company::scopeCompanyables(Department::select(
$departments = Department::select(
'departments.id',
'departments.name',
'departments.phone',
@@ -37,8 +37,8 @@ class DepartmentsController extends Controller
'departments.manager_id',
'departments.created_at',
'departments.updated_at',
'departments.image'),
"company_id", "departments")->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
'departments.image'
)->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
if ($request->filled('search')) {
$departments = $departments->TextSearch($request->input('search'));
+10 -16
View File
@@ -13,7 +13,6 @@ use App\Http\Transformers\SelectlistTransformer;
use App\Http\Transformers\UsersTransformer;
use App\Models\Asset;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\License;
use App\Models\User;
use App\Notifications\CurrentInventory;
@@ -37,7 +36,7 @@ class UsersController extends Controller
{
$this->authorize('view', User::class);
$allowed_columns = [
$users = User::select([
'users.activated',
'users.created_by',
'users.address',
@@ -74,12 +73,7 @@ class UsersController extends Controller
'users.vip',
'users.autoassign_licenses',
];
foreach(CustomField::where('type', User::class)->get() as $field) {
$allowed_columns[] = $field->db_column_name();
}
$users = User::select()->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count');
@@ -369,13 +363,15 @@ class UsersController extends Controller
$user->permissions = $permissions_array;
}
$tmp_pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
$user->password = bcrypt($request->get('password', $tmp_pass));
//
if ($request->filled('password')) {
$user->password = bcrypt($request->get('password'));
} else {
$user->password = $user->noPassword();
}
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
$user->customFill($request,Auth::user());
if ($user->save()) {
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
@@ -466,9 +462,7 @@ class UsersController extends Controller
app('App\Http\Requests\ImageUploadRequest')->handleImages($user, 600, 'image', 'avatars', 'avatar');
$user->customFill($request,Auth::user());
if ($user->save()) {
// Sync group memberships:
+15 -6
View File
@@ -5,7 +5,6 @@ namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\AssetModel;
use App\Models\DefaultValuesForCustomFields;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\View;
@@ -161,7 +160,7 @@ class AssetModelsController extends Controller
$model->notes = $request->input('notes');
$model->requestable = $request->input('requestable', '0');
DefaultValuesForCustomFields::forPivot($model, Asset::class)->delete();
$this->removeCustomFieldsDefaultValues($model);
if ($request->input('fieldset_id') == '') {
$model->fieldset_id = null;
@@ -452,7 +451,7 @@ class AssetModelsController extends Controller
}
/**
* Adds default values to a model (as long as they are truthy) (does this mean I cannot set a default value of 0?)
* Adds default values to a model (as long as they are truthy)
*
* @param AssetModel $model
* @param array $defaultValues
@@ -487,12 +486,22 @@ class AssetModelsController extends Controller
}
foreach ($defaultValues as $customFieldId => $defaultValue) {
if(is_array($defaultValue)) {
$defaultValue = implode(', ', $defaultValue);
if(is_array($defaultValue)){
$model->defaultValues()->attach($customFieldId, ['default_value' => implode(', ', $defaultValue)]);
}elseif ($defaultValue) {
$model->defaultValues()->attach($customFieldId, ['default_value' => $defaultValue]);
}
DefaultValuesForCustomFields::updateOrCreate(['custom_field_id' => $customFieldId,'item_pivot_id' => $model->id], ['default_value' => $defaultValue]);
}
return true;
}
/**
* Removes all default values
*
* @return void
*/
private function removeCustomFieldsDefaultValues(AssetModel $model)
{
$model->defaultValues()->detach();
}
}
@@ -91,7 +91,8 @@ class AssetCheckoutController extends Controller
$settings = \App\Models\Setting::getSettings();
if ($settings->full_multiple_companies_support){
// We have to check whether $target->company_id is null here since locations don't have a company yet
if (($settings->full_multiple_companies_support) && ((!is_null($target->company_id)) && (!is_null($asset->company_id)))) {
if ($target->company_id != $asset->company_id){
return redirect()->to("hardware/$assetId/checkout")->with('error', trans('general.error_user_company'));
}
@@ -164,7 +164,29 @@ class AssetsController extends Controller
$asset = $request->handleImages($asset);
}
$asset->customFill($request, Auth::user()); // Update custom fields in the database.
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
$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))) {
$asset->{$field->db_column} = \Crypt::encrypt(implode(', ', $request->input($field->db_column)));
} else {
$asset->{$field->db_column} = \Crypt::encrypt($request->input($field->db_column));
}
}
} else {
if (is_array($request->input($field->db_column))) {
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
} else {
$asset->{$field->db_column} = $request->input($field->db_column);
}
}
}
}
// Validate the asset before saving
if ($asset->isValid() && $asset->save()) {
@@ -191,9 +191,11 @@ class LoginController extends Controller
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
$user->password = $user->noPassword();
if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}
$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname']; //FIXME (or TODO?) - do we need to map additional fields that we now support? E.g. country, phone, etc.
+10 -27
View File
@@ -6,12 +6,10 @@ use App\Helpers\Helper;
use App\Http\Requests\CustomFieldRequest;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Redirect;
/**
* This controller handles all actions related to Custom Asset Fields for
* the Snipe-IT Asset Management application.
@@ -23,7 +21,6 @@ use Redirect;
*/
class CustomFieldsController extends Controller
{
/**
* Returns a view with a listing of custom fields.
*
@@ -32,16 +29,12 @@ class CustomFieldsController extends Controller
* @return \Illuminate\Support\Facades\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request)
public function index()
{
$this->authorize('view', CustomField::class);
if ( $request->input('tab') == 1 ) {
// Users section, make sure to auto-create the first fieldset if so
CustomFieldset::firstOrCreate(['type' => Helper::$itemtypes_having_custom_fields[1]], ['name' => 'default']);
}
$fieldsets = CustomFieldset::with('fields')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab',0)])->get(); //cannot eager-load 'customizable' because it's not a relation
$fields = CustomField::with('fieldset')->where("type", Helper::$itemtypes_having_custom_fields[$request->get('tab',0)])->get();
$fieldsets = CustomFieldset::with('fields', 'models')->get();
$fields = CustomField::with('fieldset')->get();
return view('custom_fields.index')->with('custom_fieldsets', $fieldsets)->with('custom_fields', $fields);
}
@@ -74,7 +67,7 @@ class CustomFieldsController extends Controller
public function create(Request $request)
{
$this->authorize('create', CustomField::class);
$fieldsets = CustomFieldset::where('type', Helper::$itemtypes_having_custom_fields[$request->get('tab')])->get();
$fieldsets = CustomFieldset::get();
return view('custom_fields.fields.edit', [
'predefinedFormats' => Helper::predefined_formats(),
@@ -117,10 +110,8 @@ class CustomFieldsController extends Controller
"display_in_user_view" => $display_in_user_view,
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->get("show_in_listview", 0),
"user_id" => Auth::id(),
"user_id" => Auth::id()
]);
// not mass-assignable; must be manual
$field->type = Helper::$itemtypes_having_custom_fields[$request->get('tab')];
if ($request->filled('custom_format')) {
@@ -133,17 +124,14 @@ class CustomFieldsController extends Controller
// Sync fields with fieldsets
$fieldset_array = $request->input('associate_fieldsets');
if ($request->get('tab') == 1 ) {
$fieldset_array = [CustomFieldset::firstOrCreate(['type' => User::class],['name' => 'default'])->id => true];
}
if (($request->has('associate_fieldsets') || $request->get('tab') == 1) && (is_array($fieldset_array))) {
if ($request->has('associate_fieldsets') && (is_array($fieldset_array))) {
$field->fieldset()->sync(array_keys($fieldset_array));
} else {
$field->fieldset()->sync([]);
}
return redirect()->route('fields.index',['tab' => $request->get('tab',0)])->with('success', trans('admin/custom_fields/message.field.create.success'));
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.create.success'));
}
return redirect()->back()->with('selected_fieldsets', $request->input('associate_fieldsets'))->withInput()
@@ -196,17 +184,12 @@ class CustomFieldsController extends Controller
if ($field = CustomField::find($field_id)) {
$this->authorize('delete', $field);
if ($field->type == User::class) {
$field->fieldset()->detach(); // remove from 'default' group (and others, if they exist in the future!)
}
if (($field->fieldset) && ($field->fieldset->count() > 0)) {
return redirect()->back()->withErrors(['message' => 'Field is in-use']);
}
$type = $field->type;
$field->delete();
return redirect()->route('fields.index',['tab' => array_search($type, Helper::$itemtypes_having_custom_fields)])
->with('success', trans('admin/custom_fields/message.field.delete.success'));
return redirect()->route("fields.index")
->with("success", trans('admin/custom_fields/message.field.delete.success'));
}
return redirect()->back()->withErrors(['message' => 'Field does not exist']);
@@ -306,7 +289,7 @@ class CustomFieldsController extends Controller
$field->fieldset()->sync([]);
}
return redirect()->route('fields.index',['tab' => $request->get('tab',0)])->with('success', trans('admin/custom_fields/message.field.update.success'));
return redirect()->route('fields.index')->with('success', trans('admin/custom_fields/message.field.update.success'));
}
return redirect()->back()->withInput()->with('error', trans('admin/custom_fields/message.field.update.error'));
@@ -2,7 +2,6 @@
namespace App\Http\Controllers;
use App\Helpers\Helper;
use App\Models\AssetModel;
use App\Models\CustomField;
use App\Models\CustomFieldset;
@@ -39,7 +38,7 @@ class CustomFieldsetsController extends Controller
* @throws \Illuminate\Auth\Access\AuthorizationException
* @since [v1.8]
*/
public function show( $id)
public function show($id)
{
$cfset = CustomFieldset::with('fields')
->where('id', '=', $id)->orderBy('id', 'ASC')->first();
@@ -47,7 +46,7 @@ class CustomFieldsetsController extends Controller
$this->authorize('view', $cfset);
if ($cfset) {
$custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::where('type', $cfset->type)->pluck('name', 'id')->toArray();
$custom_fields_list = ['' => 'Add New Field to Fieldset'] + CustomField::pluck('name', 'id')->toArray();
$maxid = 0;
foreach ($cfset->fields as $field) {
@@ -97,8 +96,6 @@ class CustomFieldsetsController extends Controller
$fieldset = new CustomFieldset([
'name' => $request->get('name'),
'user_id' => Auth::user()->id,
'type' => Helper::$itemtypes_having_custom_fields[$request->get('tab')]
// 'sub' =>
]);
$validator = Validator::make($request->all(), $fieldset->rules);
+1 -1
View File
@@ -31,7 +31,7 @@ class LabelsController extends Controller
$exampleAsset->id = 999999;
$exampleAsset->name = 'JEN-867-5309';
$exampleAsset->asset_tag = 'TCA-00001';
$exampleAsset->asset_tag = '100001';
$exampleAsset->serial = 'SN9876543210';
$exampleAsset->company = new Company();
@@ -76,7 +76,7 @@ class LicenseCheckinController extends Controller
// Declare the rules for the form validation
$rules = [
'note' => 'string|nullable',
'notes' => 'string|nullable',
];
// Create a new validator instance from our validation rules
@@ -97,6 +97,7 @@ class LicenseCheckinController extends Controller
// Update the asset data
$licenseSeat->assigned_to = null;
$licenseSeat->asset_id = null;
$licenseSeat->notes = $request->input('notes');
// Was the asset updated?
if ($licenseSeat->save()) {
@@ -128,6 +129,13 @@ class LicenseCheckinController extends Controller
$license = License::findOrFail($licenseId);
$this->authorize('checkin', $license);
if (! $license->reassignable) {
// Not allowed to checkin
Session::flash('error', 'License not reassignable.');
return redirect()->back()->withInput();
}
$licenseSeatsByUser = LicenseSeat::where('license_id', '=', $licenseId)
->whereNotNull('assigned_to')
->with('user')
@@ -63,6 +63,7 @@ class LicenseCheckoutController extends Controller
$licenseSeat = $this->findLicenseSeatToCheckout($license, $seatId);
$licenseSeat->user_id = Auth::id();
$licenseSeat->notes = $request->input('notes');
$checkoutMethod = 'checkoutTo'.ucwords(request('checkout_to_type'));
@@ -133,9 +133,6 @@ class UsersController extends Controller
// we have to invoke the
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
\Log::info("About to call customFill, in the 'store' controller!!!");
$user->customFill($request, Auth::user());
if ($user->save()) {
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
@@ -303,8 +300,6 @@ class UsersController extends Controller
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
\Log::debug("calling custom fill from the UPDATE method!");
$user->customFill($request, Auth::user());
//\Log::debug(print_r($user, true));
// Was the user updated?
+2 -1
View File
@@ -18,12 +18,13 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Fideloper\Proxy\TrustProxies::class,
\App\Http\Middleware\CheckForSetup::class,
\App\Http\Middleware\CheckForDebug::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\SecurityHeaders::class,
\App\Http\Middleware\PreventBackHistory::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Fruitcake\Cors\HandleCors::class,
];
@@ -2,8 +2,6 @@
namespace App\Http\Livewire;
use App\Models\Asset;
use App\Models\DefaultValuesForCustomFields;
use Livewire\Component;
use App\Models\CustomFieldset;
@@ -32,7 +30,7 @@ class CustomFieldSetDefaultValuesForModel extends Component
$this->fields = CustomFieldset::find($this->fieldset_id)->fields;
}
$this->add_default_values = (DefaultValuesForCustomFields::forPivot($this->model, Asset::class)->count() > 0);
$this->add_default_values = ($this->model->defaultValues->count() > 0);
}
public function updatedFieldsetId()
+17 -1
View File
@@ -100,7 +100,7 @@ class Importer extends Component
if ($type == "asset") {
// add Custom Fields after a horizontal line
$results['-'] = "———" . trans('admin/custom_fields/general.custom_fields') . "———’";
foreach (CustomField::where('type', \App\Models\Asset::class)->orderBy('name')->get() as $field) { // TODO - generalize?
foreach (CustomField::orderBy('name')->get() as $field) {
$results[$field->db_column_name()] = $field->name;
}
}
@@ -275,6 +275,7 @@ class Importer extends Component
'license_email' => trans('admin/licenses/form.to_email'),
'license_name' => trans('admin/licenses/form.to_name'),
'purchase_order' => trans('admin/licenses/form.purchase_order'),
'order_number' => trans('general.order_number'),
'reassignable' => trans('admin/licenses/form.reassignable'),
'seats' => trans('admin/licenses/form.seats'),
'notes' => trans('general.notes'),
@@ -484,8 +485,17 @@ class Importer extends Component
public function selectFile($id)
{
$this->clearMessage();
$this->activeFile = Import::find($id);
if (!$this->activeFile) {
$this->message = trans('admin/hardware/message.import.file_missing');
$this->message_type = 'danger';
return;
}
$this->field_map = null;
foreach($this->activeFile->header_row as $element) {
if(isset($this->activeFile->field_map[$element])) {
@@ -520,6 +530,12 @@ class Importer extends Component
}
}
public function clearMessage()
{
$this->message = null;
$this->message_type = null;
}
public function render()
{
$this->files = Import::orderBy('id','desc')->get(); //HACK - slows down renders.
+20 -16
View File
@@ -12,7 +12,7 @@ class SlackSettingsForm extends Component
public $webhook_endpoint;
public $webhook_channel;
public $webhook_botname;
public $isDisabled ='' ;
public $isDisabled ='disabled' ;
public $webhook_name;
public $webhook_link;
public $webhook_placeholder;
@@ -22,11 +22,17 @@ class SlackSettingsForm extends Component
public Setting $setting;
public $webhook_endpoint_rules;
protected $rules = [
'webhook_endpoint' => 'url|required_with:webhook_channel|starts_with:https://hooks.slack.com/services|nullable',
'webhook_endpoint' => 'required_with:webhook_channel|starts_with:http://,https://,ftp://,irc://,https://hooks.slack.com/services/|url|nullable',
'webhook_channel' => 'required_with:webhook_endpoint|starts_with:#|nullable',
'webhook_botname' => 'string|nullable',
];
public $messages = [
'webhook_endpoint.starts_with' => 'your webhook endpoint should begin with http://, https:// or other protocol.',
];
public function mount() {
$this->webhook_text= [
@@ -55,9 +61,7 @@ class SlackSettingsForm extends Component
$this->webhook_botname = $this->setting->webhook_botname;
$this->webhook_options = $this->setting->webhook_selected;
if($this->setting->webhook_selected == 'general'){
$this->isDisabled='';
}
if($this->setting->webhook_endpoint != null && $this->setting->webhook_channel != null){
$this->isDisabled= '';
}
@@ -65,9 +69,8 @@ class SlackSettingsForm extends Component
}
public function updated($field) {
if($this->webhook_selected != 'general') {
$this->validateOnly($field, $this->rules);
}
}
public function updatedWebhookSelected() {
@@ -82,7 +85,6 @@ class SlackSettingsForm extends Component
}
private function isButtonDisabled() {
if($this->webhook_selected == 'slack') {
if (empty($this->webhook_endpoint)) {
$this->isDisabled = 'disabled';
$this->save_button = trans('admin/settings/general.webhook_presave');
@@ -91,8 +93,6 @@ class SlackSettingsForm extends Component
$this->isDisabled = 'disabled';
$this->save_button = trans('admin/settings/general.webhook_presave');
}
}
}
public function render()
@@ -108,6 +108,7 @@ class SlackSettingsForm extends Component
'defaults' => [
'exceptions' => false,
],
'allow_redirects' => false,
]);
$payload = json_encode(
@@ -116,18 +117,23 @@ class SlackSettingsForm extends Component
'text' => trans('general.webhook_test_msg', ['app' => $this->webhook_name]),
'username' => e($this->webhook_botname),
'icon_emoji' => ':heart:',
]);
try {
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload]);
$webhook->post($this->webhook_endpoint, ['body' => $payload]);
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
}
$this->isDisabled='';
$this->save_button = trans('general.save');
return session()->flash('success' , 'Your '.$this->webhook_name.' Integration works!');
return session()->flash('success' , trans('admin/settings/message.webhook.success', ['webhook_name' => $this->webhook_name]));
} catch (\Exception $e) {
$this->isDisabled= 'disabled';
$this->isDisabled='disabled';
$this->save_button = trans('admin/settings/general.webhook_presave');
return session()->flash('error' , trans('admin/settings/message.webhook.error', ['error_message' => $e->getMessage(), 'app' => $this->webhook_name]));
}
@@ -158,9 +164,7 @@ class SlackSettingsForm extends Component
if (Helper::isDemoMode()) {
session()->flash('error',trans('general.feature_disabled'));
} else {
if ($this->webhook_selected != 'general') {
$this->validate($this->rules);
}
$this->validate($this->rules);
$this->setting->webhook_selected = $this->webhook_selected;
$this->setting->webhook_endpoint = $this->webhook_endpoint;
+3 -8
View File
@@ -2,7 +2,7 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
@@ -10,7 +10,7 @@ class TrustProxies extends Middleware
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
* @var array|string|null
*/
protected $proxies;
@@ -19,10 +19,5 @@ class TrustProxies extends Middleware
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
}
-2
View File
@@ -34,14 +34,12 @@ class CustomFieldRequest extends FormRequest
case 'POST':
{
$rules['name'] = 'required|unique:custom_fields';
$rules['tab'] = 'required';
break;
}
// Save all fields
case 'PUT':
$rules['name'] = 'required';
$rules['tab'] = 'required';
break;
// Save only what's passed
+23 -10
View File
@@ -43,9 +43,10 @@ class ActionlogsTransformer
public function transformActionlog (Actionlog $actionlog, $settings = null)
{
$icon = $actionlog->present()->icon();
$custom_field = CustomField::all();
$custom_fields = CustomField::all();
if ($actionlog->filename!='') {
$icon = e(\App\Helpers\Helper::filetype_icon($actionlog->filename));
$icon = Helper::filetype_icon($actionlog->filename);
}
// This is necessary since we can't escape special characters within a JSON object
@@ -55,17 +56,29 @@ class ActionlogsTransformer
$clean_meta = [];
if ($meta_array) {
foreach ($meta_array as $fieldname => $fieldata) {
if( str_starts_with($fieldname, '_snipeit_')){
if( $custom_field->where('db_column', '=', $fieldname)->where('field_encrypted', true)){
$clean_meta[$fieldname]['old'] = "encrypted";
$clean_meta[$fieldname]['new'] = "encrypted";
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
// this is a custom field
if (str_starts_with($fieldname, '_snipeit_')) {
foreach ($custom_fields as $custom_field) {
if ($custom_field->db_column == $fieldname) {
if ($custom_field->field_encrypted == '1') {
$clean_meta[$fieldname]['old'] = "************";
$clean_meta[$fieldname]['new'] = "************";
}
}
}
}
else {
$clean_meta[$fieldname]['old'] = $this->clean_field($fieldata->old);
$clean_meta[$fieldname]['new'] = $this->clean_field($fieldata->new);
}
}
}
+43 -2
View File
@@ -2,7 +2,6 @@
namespace App\Http\Transformers;
use App\Helpers\CustomFieldHelper;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\Setting;
@@ -97,7 +96,49 @@ class AssetsTransformer
];
$array['custom_fields'] = CustomFieldHelper::transform($asset->model->fieldset,$asset);
if (($asset->model) && ($asset->model->fieldset) && ($asset->model->fieldset->fields->count() > 0)) {
$fields_array = [];
foreach ($asset->model->fieldset->fields as $field) {
if ($field->isFieldDecryptable($asset->{$field->db_column})) {
$decrypted = Helper::gracefulDecrypt($field, $asset->{$field->db_column});
$value = (Gate::allows('assets.view.encrypted_custom_fields')) ? $decrypted : strtoupper(trans('admin/custom_fields/general.encrypted'));
if ($field->format == 'DATE'){
if (Gate::allows('assets.view.encrypted_custom_fields')){
$value = Helper::getFormattedDateObject($value, 'date', false);
} else {
$value = strtoupper(trans('admin/custom_fields/general.encrypted'));
}
}
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'field_format' => $field->format,
'element' => $field->element,
];
} else {
$value = $asset->{$field->db_column};
if (($field->format == 'DATE') && (!is_null($value)) && ($value!='')){
$value = Helper::getFormattedDateObject($value, 'date', false);
}
$fields_array[$field->name] = [
'field' => e($field->db_column),
'value' => e($value),
'field_format' => $field->format,
'element' => $field->element,
];
}
$array['custom_fields'] = $fields_array;
}
} else {
$array['custom_fields'] = new \stdClass; // HACK to force generation of empty object instead of empty list
}
$permissions_array['available_actions'] = [
'checkout' => ($asset->deleted_at=='' && Gate::allows('checkout', Asset::class)) ? true : false,
@@ -3,8 +3,6 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\CustomFieldset;
use Illuminate\Database\Eloquent\Collection;
@@ -23,13 +21,8 @@ class CustomFieldsetsTransformer
public function transformCustomFieldset(CustomFieldset $fieldset)
{
$fields = $fieldset->fields;
$models = [];
$models = $fieldset->models;
$modelsArray = [];
if ($fieldset->type == Asset::class) {
\Log::debug("Item pivot id is: ".$fieldset->item_pivot_id);
$models = AssetModel::where('fieldset_id', $fieldset->id)->get();
\Log::debug("And the models object count is: ".$models->count());
}
foreach ($models as $model) {
$modelsArray[] = [
@@ -37,21 +30,15 @@ class CustomFieldsetsTransformer
'name' => e($model->name),
];
}
\Log::debug("Models array is: ".print_r($modelsArray,true));
$array = [
'id' => (int) $fieldset->id,
'name' => e($fieldset->name),
'fields' => (new CustomFieldsTransformer)->transformCustomFields($fields, $fieldset->fields_count),
'customizables' => (new DatatablesTransformer)->transformDatatables($fieldset->customizables(),count($fieldset->customizables())),
'models' => (new DatatablesTransformer)->transformDatatables($modelsArray, $fieldset->models_count),
'created_at' => Helper::getFormattedDateObject($fieldset->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($fieldset->updated_at, 'datetime'),
'type' => $fieldset->type,
];
if ($fieldset->type == Asset::class) {
// TODO - removeme - legacy column just for Assets?
$array['models'] = (new DatatablesTransformer)->transformDatatables($modelsArray, count($modelsArray));
}
return $array;
}
@@ -45,6 +45,7 @@ class LicenseSeatsTransformer
'name'=> e($seat->location()->name),
] : null,
'reassignable' => (bool) $seat->license->reassignable,
'notes' => e($seat->notes),
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
];
@@ -2,10 +2,7 @@
namespace App\Http\Transformers;
use App\Helpers\CustomFieldHelper;
use App\Helpers\Helper;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
@@ -80,8 +77,6 @@ class UsersTransformer
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
$array['custom_fields'] = CustomFieldHelper::transform(CustomFieldset::where('type',User::class)->first(), $user);
$permissions_array['available_actions'] = [
'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')),
'delete' => (Gate::allows('delete', User::class) && ($user->assets_count == 0) && ($user->licenses_count == 0) && ($user->accessories_count == 0)),
+3 -4
View File
@@ -23,20 +23,19 @@ class AssetImporter extends ItemImporter
// ItemImporter handles the general fetching.
parent::handle($row);
// FIXME : YUP!!!!! This shit needs to go (?) Yeah?
if ($this->customFields) {
foreach ($this->customFields as $customField) {
$customFieldValue = $this->array_smart_custom_field_fetch($row, $customField); // TODO/FIXME - this might require a new 'mode' on customFill()?
$customFieldValue = $this->array_smart_custom_field_fetch($row, $customField);
if ($customFieldValue) {
if ($customField->field_encrypted == 1) { // FIXME - repeated code.
if ($customField->field_encrypted == 1) {
$this->item['custom_fields'][$customField->db_column_name()] = \Crypt::encrypt($customFieldValue);
$this->log('Custom Field '.$customField->name.': '.\Crypt::encrypt($customFieldValue));
} else {
$this->item['custom_fields'][$customField->db_column_name()] = $customFieldValue;
$this->log('Custom Field '.$customField->name.': '.$customFieldValue);
}
} else { // FIXME - think this through? Do we want to blank this? Is that how other stuff works?
} else {
// Clear out previous data.
$this->item['custom_fields'][$customField->db_column_name()] = null;
}
+2 -2
View File
@@ -28,7 +28,7 @@ abstract class Importer
/**
* Default Map of item fields->csv names
*
* This has been moved into Livewire/Importer.php to be more granular.
* This has been moved into app/Http/Livewire/Importer.php to be more granular.
* @todo - remove references to this property since we don't use it anymore.
*
* @var array
@@ -120,7 +120,7 @@ abstract class Importer
* @author Daniel Meltzer
* @since 5.0
*/
protected function populateCustomFields($headerRow) // FIXME - what in the actual fuck is this.
protected function populateCustomFields($headerRow)
{
// Stolen From https://adamwathan.me/2016/07/14/customizing-keys-when-mapping-collections/
// This 'inverts' the fields such that we have a collection of fields indexed by name.
+1
View File
@@ -65,6 +65,7 @@ class LicenseImporter extends ItemImporter
$this->item['license_name'] = $this->findCsvMatch($row, 'license_name');
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
$this->item['order_number'] = $this->findCsvMatch($row, 'order_number');
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
$this->item['manufacturer'] = $this->createOrFetchManufacturer($this->findCsvMatch($row, 'manufacturer'));
+12 -8
View File
@@ -79,13 +79,15 @@ class CheckoutableListener
/**
* Send the appropriate notification
*/
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
->where('assigned_to_id', $event->checkedOutTo->id)
->get();
if ($event->checkedOutTo && $event->checkoutable){
$acceptances = CheckoutAcceptance::where('checkoutable_id', $event->checkoutable->id)
->where('assigned_to_id', $event->checkedOutTo->id)
->get();
foreach($acceptances as $acceptance){
if($acceptance->isPending()){
$acceptance->delete();
foreach($acceptances as $acceptance){
if($acceptance->isPending()){
$acceptance->delete();
}
}
}
@@ -142,9 +144,11 @@ class CheckoutableListener
$notifiables = collect();
/**
* Notify the user who checked out the item
* Notify who checked out the item as long as the model can route notifications
*/
$notifiables->push($event->checkedOutTo);
if (method_exists($event->checkedOutTo, 'routeNotificationFor')) {
$notifiables->push($event->checkedOutTo);
}
/**
* Notify Admin users if the settings is activated
+36 -16
View File
@@ -9,8 +9,6 @@ use App\Helpers\Helper;
use App\Http\Traits\UniqueSerialTrait;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\Acceptable;
use App\Models\Traits\Customizable;
use App\Models\Traits\HasCustomFields;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use AssetPresenter;
@@ -20,7 +18,6 @@ use DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
@@ -41,19 +38,8 @@ class Asset extends Depreciable
public const ASSET = 'asset';
public const USER = 'user';
use Acceptable, HasCustomFields;
use Acceptable;
public function getFieldsetKey(): object|int|null {
return $this->model;
}
public static function getFieldsetUsers(int $fieldset_id): array {
$models = [];
foreach(AssetModel::where("fieldset_id",$fieldset_id)->get() as $model) {
$models[route('models.show', $model->id)] = $model->name.(($model->model_number) ? ' ('.$model->model_number.')' : '');
}
return $models;
}
/**
* Run after the checkout acceptance was declined by the user
*
@@ -202,6 +188,40 @@ class Asset extends Depreciable
$this->attributes['expected_checkin'] = $value;
}
/**
* This handles the custom field validation for assets
*
* @var array
*/
public function save(array $params = [])
{
if ($this->model_id != '') {
$model = AssetModel::find($this->model_id);
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field){
if($field->format == 'BOOLEAN'){
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
$this->rules += $model->fieldset->validation_rules();
foreach ($this->model->fieldset->fields as $field){
if($field->format == 'BOOLEAN'){
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
}
}
return parent::save($params);
}
public function getDisplayNameAttribute()
{
return $this->present()->name();
@@ -1503,7 +1523,7 @@ class Asset extends Depreciable
*
* In short, this set of statements tells the query builder to ONLY query against an
* actual field that's being passed if it doesn't meet known relational fields. This
* allows us to query custom fields directly in the assets table
* allows us to query custom fields directly in the assetsv table
* (regardless of their name) and *skip* any fields that we already know can only be
* searched through relational searches that we do earlier in this method.
*
+17 -1
View File
@@ -150,7 +150,6 @@ class AssetModel extends SnipeModel
*/
public function fieldset()
{
// this is actually OK - we don't *need* to do this, but it's okay to make references from Model to fieldset
return $this->belongsTo(\App\Models\CustomFieldset::class, 'fieldset_id');
}
@@ -159,6 +158,18 @@ class AssetModel extends SnipeModel
return $this->fieldset()->first()->fields();
}
/**
* Establishes the model -> custom field default values relationship
*
* @author hannah tinkler
* @since [v4.3]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function defaultValues()
{
return $this->belongsToMany(\App\Models\CustomField::class, 'models_custom_fields')->withPivot('default_value');
}
/**
* Gets the full url for the image
*
@@ -280,4 +291,9 @@ class AssetModel extends SnipeModel
{
return $query->leftJoin('categories', 'models.category_id', '=', 'categories.id')->orderBy('categories.name', $order);
}
public function scopeOrderFieldset($query, $order)
{
return $query->leftJoin('custom_fieldsets', 'models.fieldset_id', '=', 'custom_fieldsets.id')->orderBy('custom_fieldsets.name', $order);
}
}
+42 -30
View File
@@ -16,7 +16,7 @@ class CustomField extends Model
UniqueUndeletedTrait;
/**
* Custom field predefined formats
* Custom field predfined formats
*
* @var array
*/
@@ -53,7 +53,6 @@ class CustomField extends Model
'field_encrypted' => 'nullable|boolean',
'auto_add_to_fieldsets' => 'boolean',
'show_in_listview' => 'boolean',
'type' => 'required'
];
/**
@@ -76,19 +75,30 @@ class CustomField extends Model
];
/**
* This is confusing, since it's actually the custom fields table that
* we're usually modifying, but since we alter the assets table, we have to
* say that here, otherwise the new fields get added onto the custom fields
* table instead of the assets table.
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v3.0]
*/
public static $table_name = 'assets';
/**
* Convert the custom field's name property to a db-safe string.
*
* We could probably have used str_slug() here but not sure what it would
* do with previously existing values. - @snipe
*
* @return string
* @since [v3.4]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.4]
* @return string
*/
public static function name_to_db_name($name)
{
return '_snipeit_' . preg_replace('/[^a-zA-Z0-9]/', '_', strtolower($name));
return '_snipeit_'.preg_replace('/[^a-zA-Z0-9]/', '_', strtolower($name));
}
/**
@@ -99,22 +109,23 @@ class CustomField extends Model
* if they have changed, so we handle that here so that we don't have to remember
* to do it in the controllers.
*
* @return bool
* @since [v3.4]
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.4]
* @return bool
*/
public static function boot()
{
parent::boot();
self::created(function ($custom_field) {
// Column already exists on the assets table - nothing to do here.
// This *shouldn't* happen in the wild.
if (Schema::hasColumn($custom_field->getTableName(), $custom_field->db_column)) {
if (Schema::hasColumn(self::$table_name, $custom_field->db_column)) {
return false;
}
// Update the column name in the assets table
Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) {
Schema::table(self::$table_name, function ($table) use ($custom_field) {
$table->text($custom_field->convertUnicodeDbSlug())->nullable();
});
@@ -127,7 +138,7 @@ class CustomField extends Model
// Column already exists on the assets table - nothing to do here.
if ($custom_field->isDirty('name')) {
if (Schema::hasColumn($custom_field->getTableName(), $custom_field->convertUnicodeDbSlug())) {
if (Schema::hasColumn(self::$table_name, $custom_field->convertUnicodeDbSlug())) {
return true;
}
@@ -137,7 +148,7 @@ class CustomField extends Model
$platform->registerDoctrineTypeMapping('enum', 'string');
// Rename the field if the name has changed
Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) {
Schema::table(self::$table_name, function ($table) use ($custom_field) {
$table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug());
});
@@ -153,19 +164,12 @@ class CustomField extends Model
// Drop the assets column if we've deleted it from custom fields
self::deleting(function ($custom_field) {
return Schema::table($custom_field->getTableName(), function ($table) use ($custom_field) {
return Schema::table(self::$table_name, function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
});
}
public function getTableName()
{
$type = $this->type;
$instance = new $type();
return $instance->getTable();
}
/**
* Establishes the customfield -> fieldset relationship
*
@@ -196,23 +200,31 @@ class CustomField extends Model
}
/**
* Returns the default value for a given 'item' using the defaultValues
* Establishes the customfield -> default values relationship
*
* @author Hannah Tinkler
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function defaultValues()
{
return $this->belongsToMany(\App\Models\AssetModel::class, 'models_custom_fields')->withPivot('default_value');
}
/**
* Returns the default value for a given model using the defaultValues
* relationship
*
* @param int $modelId
* @return string
*/
public function defaultValue($pivot_id)
public function defaultValue($modelId)
{
/*
below, you might think you need to add:
where('type', $this->type),
but the type can be inferred from by the custom_field itself (which also has a type)
can't use forPivot() here because we don't have an object yet. (TODO?)
*/
DefaultValuesForCustomFields::where('item_pivot_id', $pivot_id)->where('custom_field_id',$this->id)->first()?->default_value; //TODO - php8-only operator!
return $this->defaultValues->filter(function ($item) use ($modelId) {
return $item->pivot->asset_model_id == $modelId;
})->map(function ($item) {
return $item->pivot->default_value;
})->first();
}
/**
+4 -13
View File
@@ -20,7 +20,6 @@ class CustomFieldset extends Model
*/
public $rules = [
'name' => 'required|unique:custom_fieldsets',
''
];
/**
@@ -49,13 +48,11 @@ class CustomFieldset extends Model
*
* @author [Brady Wetherington] [<uberbrady@gmail.com>]
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Collection
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function customizables() // TODO - I don't like this name, but I can't think of anything better
public function models()
{
$customizable_class_name = $this->type; //TODO - copypasta from Customizable trait?
\Log::debug("Customizable Class name is: ".$customizable_class_name);
return $customizable_class_name::getFieldsetUsers($this->id);
return $this->hasMany(\App\Models\AssetModel::class, 'fieldset_id');
}
/**
@@ -80,7 +77,6 @@ class CustomFieldset extends Model
*/
public function validation_rules()
{
\Log::debug("CALLING validation_rules FOR customfiledsets!");
$rules = [];
foreach ($this->fields as $field) {
$rule = [];
@@ -94,12 +90,7 @@ class CustomFieldset extends Model
$rule[] = 'unique_undeleted';
}
\Log::debug("Field Format for".$field->name." is: ".$field->format);
if($field->format == 'DATE') { //we do a weird mutator thing, it's confusing - but, yes, it's all-caps
$rule[] = 'date_format:Y-m-d';
} else {
array_push($rule, $field->attributes['format']);
}
array_push($rule, $field->attributes['format']);
$rules[$field->db_column_name()] = $rule;
}
@@ -1,34 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Watson\Validating\ValidatingTrait;
class DefaultValuesForCustomFields extends Model
{
use HasFactory, ValidatingTrait;
protected $rules = [
'type' => 'required'
];
public $timestamps = false;
public function field() {
return $this->belongsTo('custom_fields');
}
// There is, effectively, another 'relation' here, but it's weirdly polymorphic
// and impossible to represent in Laravel.
// we have a 'type', and we have an 'item_pivot_id' -
// For example, in Assets the 'type' would be App\Models\Asset, and the 'item_pivot_id' would be a model_id
// I can't come up with any way to represent this in Laravel/Eloquent
// TODO: might be getting overly-fancy here; maybe just want to do an ID? Instead of an Eloquent Model?
public function scopeForPivot(Builder $query, Model $item, string $class) {
return $query->where('item_pivot_id', $item->id)->where('type', $class);
}
}
+1
View File
@@ -9,6 +9,7 @@ use Watson\Validating\ValidatingTrait;
class Department extends SnipeModel
{
use CompanyableTrait;
use HasFactory;
/**
+2 -1
View File
@@ -7,6 +7,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use TCPDF;
use TCPDF_STATIC;
use TypeError;
/**
* Model for Labels.
@@ -372,7 +373,7 @@ abstract class Label
if (empty($value)) return;
try {
$pdf->write1DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
} catch (\Exception $e) {
} catch (\Exception|TypeError $e) {
\Log::error('The 1D barcode ' . $value . ' is not compliant with the barcode type '. $type);
}
}
+2 -5
View File
@@ -252,13 +252,10 @@ class Ldap extends Model
$user->last_name = $item['lastname'];
$user->username = $item['username'];
$user->email = $item['email'];
$user->password = $user->noPassword();
if (Setting::getSettings()->ldap_pw_sync == '1') {
$user->password = bcrypt($password);
} else {
$pass = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 25);
$user->password = bcrypt($pass);
}
$user->activated = 1;
@@ -268,7 +265,7 @@ class Ldap extends Model
if ($user->save()) {
return $user;
} else {
LOG::debug('Could not create user.'.$user->getErrors());
\Log::debug('Could not create user.'.$user->getErrors());
throw new Exception('Could not create user: '.$user->getErrors());
}
}
+1 -2
View File
@@ -9,8 +9,7 @@ class SCIMUser extends User
protected $throwValidationExceptions = true; // we want model-level validation to fully THROW, not just return false
public function __construct(array $attributes = []) {
$attributes['password'] = "*NO PASSWORD*";
// $attributes['activated'] = 1;
$attributes['password'] = $this->noPassword();
parent::__construct($attributes);
}
}
-137
View File
@@ -1,137 +0,0 @@
<?php
namespace App\Models\Traits;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Support\Collection;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Event;
use App\Models\DefaultValuesForCustomFields;
/*********************************
* Trait HasCustomFields
* @package App\Models\Traits
*
* How to use: declare a PHP function getFieldset that will return your fieldset (not the ID, the actual set)
*
*/
trait HasCustomFields
{
protected static function bootHasCustomFields()
{
// https://tech.chrishardie.com/2022/define-fire-listen-custom-laravel-model-events-trait/
static::registerModelEvent('validating', function ($model, $event) {
\Log::debug("Uh, something happened? Something good, maybe?");
\Log::debug("model: $model, event: $event");
self::augmentValidationRulesForCustomFields($model);
});
}
/***************
* @return CustomFieldset|null
*
* This function by default will use the "getFieldsetKey()" method to
* return the customFieldset (or null) for this particular item. If
* necessary, you can override this method if your getFieldsetKey()
* cannot respond to `->fieldset` or `->id`.
*/
public function getFieldset(): ?CustomFieldset {
$pivot = $this->getFieldsetKey();
if(is_int($pivot)) { //why does this look just like the other thing? (below, look for is_int()
return CustomFieldset::find($pivot);
}
return $pivot->fieldset;
}
/**********************
* @return Object|int|null
* (if this is in PHP 8.0, can we just put that as the signature?)
*
* This is the main method you have to override. It should either return an
* Object who you can call `->fieldset` on and get a fieldset object, and also
* be able to call `->id` on to get a unique key to be able to show custom fields.
* For example, for Assets, the element that is returned is the 'model' for the Asset.
* For something like Users, which will probably have only one universal set of custom fields,
* it should just return the Fieldset ID for it. Or, if there are no custom fields, it should
* return null
*/
abstract public function getFieldsetKey(): Object|int|null; // php v8 minimum, GOOD. TODO
/***********************
* @param int $fieldset_id
* @return Collection
*
* This is the main method you need to override to return a list of things that are *using* this fieldset
* The format is an array with keys: a URL, and values. So, for assets, it might return
* {
* "models/14" => "MacBook Pro 13 (model no: 12345)"
* }
*/
abstract public static function getFieldsetUsers(int $fieldset_id): array;
public static function augmentValidationRulesForCustomFields($model) {
\Log::debug("Augmenting validation rules for custom fields!!!!!!");
$fieldset = $model->getFieldset();
if ($fieldset) {
foreach ($fieldset->fields as $field){
if($field->format == 'BOOLEAN'){ // TODO - this 'feels' like entanglement of concerns?
$model->{$field->db_column} = filter_var($model->{$model->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
if(!$model->rules) {
$model->rules = [];
}
$model->rules += $model->getFieldset()->validation_rules();
\Log::debug("FINAL RULES ARE: ".print_r($model->rules,true));
}
}
public function getDefaultValue(CustomField $field)
{
$pivot = $this->getFieldsetKey(); // TODO - feels copypasta-ish?
$key_id = null;
if( is_int($pivot) ) { // TODO: *WHY* does this code repeat?!
$key_id = $pivot; // now we're done
} elseif( is_object($pivot) ) {
$key_id = $pivot?->id;
}
if(is_null($key_id)) {
return;
}
// TODO - begninng to think my custom scope really should be just an integer :/
return DefaultValuesForCustomFields::where('type',self::class)
->where('custom_field_id',$field->id)
->where('item_pivot_id',$key_id)->first()?->default_value;
}
public function customFill(Request $request, User $user, bool $shouldSetDefaults = false) {
if ($this->getFieldset()) {
foreach ($this->getFieldset()->fields as $field) {
if (is_array($request->input($field->db_column))) {
$field_value = implode(', ', $request->input($field->db_column));
} else {
$field_value = $request->input($field->db_column);
}
if ($shouldSetDefaults && (is_null($field_value) || $field_value === '')) {
$field_value = $this->getDefaultValue($field);
}
if ($field->field_encrypted == '1') {
if ($user->can('admin')) {
$this->{$field->db_column} = \Crypt::encrypt($field_value);
}
} else {
$this->{$field->db_column} = $request->input($field->db_column);
}
}
}
}
}
+16 -16
View File
@@ -3,7 +3,6 @@
namespace App\Models;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\HasCustomFields;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use DB;
@@ -32,7 +31,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
use Notifiable;
use Presentable;
use Searchable;
use HasCustomFields;
protected $hidden = ['password', 'remember_token', 'permissions', 'reset_password_code', 'persist_code'];
protected $table = 'users';
@@ -136,20 +134,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
'manager' => ['first_name', 'last_name', 'username'],
];
public function getFieldsetKey(): object|int|null
{
// TODO/FIXME - that's hardcoded text, but what language should you use?! I don't know.
// also TODO - is this going to beat on the DB too hard?
return CustomFieldset::where('type', User::class)->first()?->id;
}
public static function getFieldsetUsers(int $fieldset_id): array
{
return [
'no_idea_what_id_to_put' => 'No idea what string to put?' // FIXME obvs.
];
}
/**
* Internally check the user permission for the given section
*
@@ -472,6 +456,22 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at');
}
/**
* Set a common string when the user has been imported/synced from:
*
* - LDAP without password syncing
* - SCIM
* - CSV import where no password was provided
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.2.0]
* @return string
*/
public function noPassword()
{
return "*** NO PASSWORD ***";
}
/**
* Query builder scope to return NOT-deleted users
+16 -15
View File
@@ -2,10 +2,7 @@
namespace App\Presenters;
use App\Helpers\CustomFieldHelper;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use DateTime;
/**
@@ -145,8 +142,8 @@ class AssetPresenter extends Presenter
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'age',
'searchable' => true,
'sortable' => true,
'searchable' => false,
'sortable' => false,
'visible' => false,
'title' => trans('general.age'),
], [
@@ -285,21 +282,25 @@ class AssetPresenter extends Presenter
// models. We only pass the fieldsets that pertain to each asset (via their model) so that we
// don't junk up the REST API with tons of custom fields that don't apply
//only get fieldsets that have fields
$fieldsets = CustomFieldset::where("type", Asset::class)->whereHas('fields')->get();
$ids = [];
foreach($fieldsets as $fieldset) {
if (count($fieldset->customizables()) > 0) { //only get fieldsets that are 'in use'
$ids[] = $fieldset->id;
}
}
$fields = CustomField::whereHas('fieldset', function ($query) {
$query->whereHas('models');
})->get();
$fields = CustomField::whereIn('id',$ids)->get(); // FIXME: d'oh! this is wrong. We just got fieldsets, above. Now we're getting fields?
// Note: We do not need to e() escape the field names here, as they are already escaped when
// they are presented in the blade view. If we escape them here, custom fields with quotes in their
// name can break the listings page. - snipe
foreach ($fields as $field) {
$layout[] = CustomFieldHelper::present($field);
$layout[] = [
'field' => 'custom_fields.'.$field->db_column,
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => $field->name,
'formatter'=> 'customFieldsFormatter',
'escape' => true,
'class' => ($field->field_encrypted == '1') ? 'css-padlock' : '',
'visible' => ($field->show_in_listview == '1') ? true : false,
];
}
$layout[] = [
+8
View File
@@ -254,6 +254,14 @@ class LicensePresenter extends Presenter
'visible' => true,
'formatter' => 'locationsLinkObjFormatter',
],
[
'field' => 'notes',
'searchable' => false,
'sortable' => false,
'visible' => false,
'title' => trans('general.notes'),
'formatter' => 'notesFormatter'
],
[
'field' => 'checkincheckout',
'searchable' => false,
-30
View File
@@ -2,14 +2,8 @@
namespace App\Presenters;
use App\Helpers\CustomFieldHelper;
use App\Helpers\Helper;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
@@ -365,30 +359,6 @@ class UserPresenter extends Presenter
],
];
// TODO - FIXME - this is all copy-pasta'ed from the AssetPresenter! <start>
//only get fieldsets that have fields
$fieldsets = CustomFieldset::where("type", User::class)->whereHas('fields')->get();
$ids = [];
foreach($fieldsets as $fieldset) {
if (count($fieldset->customizables()) > 0) { //only get fieldsets that are 'in use'
\Log::debug("Found a fieldset! It's: ".$fieldset->id);
$ids[] = $fieldset->id;
} else {
\Log::debug("Didn't find fieldset: ".$fieldset->id);
}
}
$fields = CustomField::whereHas('fieldset', function (Builder $query) use($ids) {
$query->whereIn('custom_fieldsets.id', $ids);
})->get();
// Note: We do not need to e() escape the field names here, as they are already escaped when
// they are presented in the blade view. If we escape them here, custom fields with quotes in their
// name can break the listings page. - snipe
foreach ($fields as $field) {
\Log::debug("iterating through fields!");
$layout[] = CustomFieldHelper::present($field);
}
return json_encode($layout);
}
+1 -1
View File
@@ -87,7 +87,7 @@ class AuthServiceProvider extends ServiceProvider
]);
$this->registerPolicies();
//Passport::routes(); //this is no longer required in newer passport versions
Passport::routes();
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')));
+26 -13
View File
@@ -10,8 +10,14 @@
],
"license": "AGPL-3.0-or-later",
"type": "project",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/grokability/laravel-scim-server"
}
],
"require": {
"php": "8.0 - 8.2",
"php": ">=7.4.3 <8.2",
"ext-curl": "*",
"ext-fileinfo": "*",
"ext-json": "*",
@@ -23,49 +29,56 @@
"barryvdh/laravel-debugbar": "^3.6",
"barryvdh/laravel-dompdf": "^2.0",
"doctrine/cache": "^1.10",
"doctrine/common": "^2.12",
"doctrine/dbal": "^3.1",
"doctrine/inflector": "^1.3",
"doctrine/instantiator": "^1.3",
"eduardokum/laravel-mail-auto-embed": "^2.0",
"eduardokum/laravel-mail-auto-embed": "^1.0",
"enshrined/svg-sanitize": "^0.15.0",
"erusev/parsedown": "^1.7",
"spatie/laravel-ignition": "^2.0",
"facade/ignition": "^2.10",
"fideloper/proxy": "^4.3",
"fruitcake/laravel-cors": "^2.2",
"guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.5",
"javiereguiluz/easyslugger": "^1.0",
"laravel/framework": "^10.0",
"laravel/framework": "^8.46",
"laravel/helpers": "^1.4",
"laravel/passport": "^11.0",
"laravel/passport": "^10.1",
"laravel/slack-notification-channel": "^2.3",
"laravel/socialite": "^5.6",
"laravel/tinker": "^2.6",
"laravel/ui": "^4.0",
"laravel/ui": "^3.3",
"laravelcollective/html": "^6.2",
"lcobucci/clock": "^1.2.0|^2.0.0",
"lcobucci/jwt": "^3.4.5|^4.0.4",
"league/csv": "^9.7",
"league/flysystem-aws-s3-v3": "^3.0",
"league/flysystem-aws-s3-v3": "^1.0",
"league/flysystem-cached-adapter": "^1.1",
"livewire/livewire": "^2.4",
"mediconesystems/livewire-datatables": "^0.5.0",
"neitanod/forceutf8": "^2.0",
"nesbot/carbon": "^2.32",
"nunomaduro/collision": "^6.1",
"nunomaduro/collision": "^5.4",
"onelogin/php-saml": "^3.4",
"paragonie/constant_time_encoding": "^2.3",
"paragonie/sodium_compat": "^1.19",
"phpdocumentor/reflection-docblock": "^5.1",
"phpspec/prophecy": "^1.10",
"pragmarx/google2fa-laravel": "^1.3",
"rollbar/rollbar-laravel": "^8.0",
"spatie/laravel-backup": "^8.0",
"rollbar/rollbar-laravel": "^7.0",
"spatie/laravel-backup": "^6.16",
"symfony/polyfill-mbstring": "^1.22",
"tecnickcom/tc-lib-barcode": "^1.15",
"tecnickcom/tcpdf": "^6.5",
"unicodeveloper/laravel-password": "^1.0",
"watson/validating": "^8.1"
"watson/validating": "^6.1"
},
"require-dev": {
"brianium/paratest": "^v6.4.4",
"brianium/paratest": "^6.6",
"fakerphp/faker": "^1.16",
"mockery/mockery": "^1.4",
"nunomaduro/larastan": "^2.0",
"nunomaduro/larastan": "^1.0",
"nunomaduro/phpinsights": "^2.7",
"phpunit/php-token-stream": "^3.1",
"phpunit/phpunit": "^9.0",
Generated
+3933 -3037
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -239,7 +239,7 @@ return [
|
*/
'min_php' => '7.2.5',
'min_php' => '7.4.0',
/*
@@ -289,6 +289,7 @@ return [
Intervention\Image\ImageServiceProvider::class,
Collective\Html\HtmlServiceProvider::class,
Spatie\Backup\BackupServiceProvider::class,
Fideloper\Proxy\TrustedProxyServiceProvider::class,
PragmaRX\Google2FALaravel\ServiceProvider::class,
Laravel\Passport\PassportServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class,
+7
View File
@@ -35,6 +35,13 @@ return [
'files' => [
/*
* This path is used to make directories in resulting zip-file relative
* Set to false to include complete absolute path
* Example: base_path()
*/
'relative_path' => base_path(),
/*
* The list of directories and files that will be included in the backup.
*/
+1 -9
View File
@@ -1,7 +1,5 @@
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| DO NOT EDIT THIS FILE DIRECTLY.
@@ -59,12 +57,6 @@ return [
*
* @link https://symfony.com/doc/current/deployment/proxies.html
*/
'headers' => Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB,
'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_ALL,
];
+6 -6
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v6.2.0-pre',
'full_app_version' => 'v6.2.0-pre - build 11391-g319cb2305',
'build_version' => '11391',
'app_version' => 'v6.2.1',
'full_app_version' => 'v6.2.1 - build 11625-gc45ede2d1',
'build_version' => '11625',
'prerelease_version' => '',
'hash_version' => 'g319cb2305',
'full_hash' => 'v6.2.0-pre-451-g319cb2305',
'branch' => 'develop',
'hash_version' => 'gc45ede2d1',
'full_hash' => 'v6.2.1-47-gc45ede2d1',
'branch' => 'master',
);
-11
View File
@@ -338,15 +338,4 @@ class AssetFactory extends Factory
{
return $this->state(['requestable' => false]);
}
public function withComplicatedCustomFields()
{
return $this->state(function () {
return [
'model_id' => function () {
return AssetModel::where('name','complicated')->first() ?? AssetModel::factory()->complicated();
}
];
});
}
}
-10
View File
@@ -3,7 +3,6 @@
namespace Database\Factories;
use App\Models\AssetModel;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use App\Models\Depreciation;
use App\Models\Manufacturer;
@@ -430,13 +429,4 @@ class AssetModelFactory extends Factory
];
});
}
public function complicated()
{
return $this->state(function () {
return [
'name' => 'Complicated fieldset'
];
})->for(CustomFieldSet::factory()->complicated(),'fieldset');
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ class CompanyFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->company(),
'name' => $this->faker->unique()->company(),
];
}
}
+2 -22
View File
@@ -22,11 +22,10 @@ class CustomFieldFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->catchPhrase(),
'name' => $this->faker->unique()->catchPhrase(),
'format' => '',
'element' => 'text',
'auto_add_to_fieldsets' => '0',
'type' => 'App\\Models\\Asset'
];
}
@@ -75,28 +74,9 @@ class CustomFieldFactory extends Factory
{
return $this->state(function () {
return [
'name' => 'MAC Address EXPLICIT',
'name' => 'MAC Address',
'format' => 'regex:/^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/',
];
});
}
public function plainText()
{
return $this->state(function () {
return [
'name' => 'plain_text',
];
});
}
public function date()
{
return $this->state(function () {
return [
'name' => 'date',
'format' => 'date'
];
});
}
}
+1 -14
View File
@@ -2,7 +2,6 @@
namespace Database\Factories;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Database\Eloquent\Factories\Factory;
@@ -23,7 +22,7 @@ class CustomFieldsetFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->catchPhrase(),
'name' => $this->faker->unique()->catchPhrase(),
];
}
@@ -44,16 +43,4 @@ class CustomFieldsetFactory extends Factory
];
});
}
public function complicated()
{
//$mac = CustomField::factory()->macAddress()->create();
return $this->state(function () {
return [
'name' => 'complicated'
];
}) ->hasAttached(CustomField::factory()->macAddress(),['required' => false, 'order' => 0],'fields')
->hasAttached(CustomField::factory()->plainText(),['required' => true,'order' => 1],'fields')
->hasAttached(CustomField::factory()->date(),['required' => false, 'order' => 2],'fields');
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ class DepreciationFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->catchPhrase(),
'name' => $this->faker->unique()->catchPhrase(),
'user_id' => User::factory()->superuser(),
'months' => 36,
];
+1 -1
View File
@@ -23,7 +23,7 @@ class ManufacturerFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->company(),
'name' => $this->faker->unique()->company(),
'user_id' => User::factory()->superuser(),
'support_phone' => $this->faker->phoneNumber(),
'url' => $this->faker->url(),
+2 -2
View File
@@ -33,11 +33,11 @@ class UserFactory extends Factory
'permissions' => '{}',
'phone' => $this->faker->phoneNumber(),
'state' => $this->faker->stateAbbr(),
'username' => $this->faker->username(),
'username' => $this->faker->unique()->username(),
'zip' => $this->faker->postcode(),
];
}
public function firstAdmin()
{
return $this->state(function () {
@@ -23,8 +23,8 @@ function updateLegacyColumnName($customfield)
$name_to_db_name = CustomField::name_to_db_name($customfield->name);
//\Log::debug('Trying to rename '.$name_to_db_name." to ".$customfield->convertUnicodeDbSlug()."...\n");
if (Schema::hasColumn('assets', $name_to_db_name)) {
return Schema::table('assets',
if (Schema::hasColumn(CustomField::$table_name, $name_to_db_name)) {
return Schema::table(CustomField::$table_name,
function ($table) use ($name_to_db_name, $customfield) {
$table->renameColumn($name_to_db_name, $customfield->convertUnicodeDbSlug());
}
@@ -81,8 +81,8 @@ class FixUtf8CustomFieldColumnNames extends Migration
// "_snipeit_imei_1" becomes "_snipeit_imei"
$legacyColumnName = (string) Str::of($currentColumnName)->replaceMatches('/_(\d)+$/', '');
if (Schema::hasColumn('assets', $currentColumnName)) {
Schema::table('assets', function (Blueprint $table) use ($currentColumnName, $legacyColumnName) {
if (Schema::hasColumn(CustomField::$table_name, $currentColumnName)) {
Schema::table(CustomField::$table_name, function (Blueprint $table) use ($currentColumnName, $legacyColumnName) {
$table->renameColumn(
$currentColumnName,
$legacyColumnName
@@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\CustomField;
use App\Models\Asset;
class AddTypeToCustomFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('custom_fields', function (Blueprint $table) {
//
$table->text('type')->default('App\\Models\\Asset'); // TODO this default is needed for a not-nullable column, I guess? I don't like this because it will silently 'fix' errors we should properly 'fix'
});
CustomField::query()->update(['type' => Asset::class]); // TODO - is this still necessary with that 'default'?
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('custom_fields', function (Blueprint $table) {
//
$table->dropColumn('type');
});
}
}
@@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\CustomFieldset;
use App\Models\Asset;
class AddTypeToCustomFieldsets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('custom_fieldsets', function (Blueprint $table) {
//
$table->text('type')->default('App\\Models\\Asset');
});
CustomFieldset::query()->update(['type' => Asset::class]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('custom_fieldsets', function (Blueprint $table) {
//
$table->dropColumn('type');
});
}
}
@@ -1,43 +0,0 @@
<?php
use App\Models\Asset;
use App\Models\DefaultValuesForCustomFields;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTypeColumnToDefaultValuesForCustomFields extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('default_values_for_custom_fields', function (Blueprint $table) {
$table->string('type')->nullable();
$table->renameColumn('asset_model_id','item_pivot_id'); //this one works. okay. that's someting.
//$table->text('type')->nullable(false)->change();
});
DefaultValuesForCustomFields::query()->update(['type' => Asset::class]);
Schema::table('default_values_for_custom_fields', function (Blueprint $table) {
//$table->renameColumn('asset_model_id','item_pivot_id'); //this one works. okay. that's someting.
$table->string('type')->nullable(false)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('default_values_for_custom_fields', function (Blueprint $table) {
$table->dropColumn('type');
$table->renameColumn('item_pivot_id','asset_model_id');
});
}
}
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class GeneralizeDefaultValuesForCustomFields extends Migration
class FixAssetModelMinQtyNullability extends Migration
{
/**
* Run the migrations.
@@ -13,7 +13,9 @@ class GeneralizeDefaultValuesForCustomFields extends Migration
*/
public function up()
{
Schema::rename('models_custom_fields', 'default_values_for_custom_fields');
Schema::table('models', function (Blueprint $table) {
$table->integer('min_amt')->nullable()->change();
});
}
/**
@@ -23,6 +25,8 @@ class GeneralizeDefaultValuesForCustomFields extends Migration
*/
public function down()
{
Schema::rename('default_values_for_custom_fields', 'models_custom_fields');
Schema::table('models', function (Blueprint $table) {
$table->integer('min_amt')->nullable(false)->change();
});
}
}
+16576 -7884
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -37,18 +37,19 @@
"bootstrap-less": "^3.3.8",
"bootstrap-table": "1.22.1",
"chart.js": "^2.9.4",
"clipboard": "^2.0.11",
"css-loader": "^4.0.0",
"ekko-lightbox": "^5.1.1",
"imagemin": "^8.0.1",
"jquery-form-validator": "^2.3.79",
"jquery-slimscroll": "^1.3.8",
"jquery-ui": "^1.13.2",
"jquery-ui-bundle": "^1.12.1",
"jquery.iframe-transport": "^1.0.0",
"jspdf-autotable": "^3.5.30",
"less": "^4.2.0",
"less-loader": "^6.0",
"less-loader": "^5.0",
"list.js": "^1.5.0",
"morris.js": "github:morrisjs/morris.js",
"papaparse": "^4.3.3",
"select2": "4.0.13",
"sheetjs": "^2.0.0",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-6
View File
@@ -44,12 +44,6 @@
* http://jquery.org/license
*/
/*!
* vue-resource v1.5.3
* https://github.com/pagekit/vue-resource
* Released under the MIT License.
*/
/**
* @license
* Lodash <https://lodash.com/>
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -9
View File
@@ -1,9 +1,9 @@
{
"/js/build/app.js": "/js/build/app.js?id=43fc984e5d0f901e04cef2474972e97f",
"/js/build/app.js": "/js/build/app.js?id=72071a8a4dc754c61b0440d3c4119cbf",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/build/overrides.css": "/css/build/overrides.css?id=eb013ebb79d92e25ce24b0c0b53185e4",
"/css/build/app.css": "/css/build/app.css?id=ac51a0600b419996e705675b6fcf1a8b",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
"/css/build/overrides.css": "/css/build/overrides.css?id=d96bcc45dc2a4414dd9840a14b096d4f",
"/css/build/app.css": "/css/build/app.css?id=b0aa590a3a4de33d19147264fd31b743",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=f25c77ed07053646a42e9c19923d24fa",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
@@ -18,7 +18,7 @@
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/all.css": "/css/dist/all.css?id=829410b57e28448fef5c57beda0861e3",
"/css/dist/all.css": "/css/dist/all.css?id=7871ba49ce03aeb14efc98327f593e3a",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=a656b2d865fe379d8851757e8e4001ef",
@@ -29,10 +29,10 @@
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=7f63d634454e771396bce3e09dfcdbc5",
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=70ad875b2378eb850254f01dec991ade",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=d36941873b661076f146b0221f13497d",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=281bcfe26549412d128f695234961081",
"/js/build/vendor.js": "/js/build/vendor.js?id=8ac1d250496313e93744790e5138305d",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=df78f0c4cc93c29c02a41144590f6350",
"/js/dist/all.js": "/js/dist/all.js?id=b487452bd6bef7fa6201586415d383f2",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=2bd29fa7f9d666800c246a52ce708633",
"/js/build/vendor.js": "/js/build/vendor.js?id=917784d6fe54bcfe39656e0ded1b43e4",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=1f678160a05960c3087fb8263168ff41",
"/js/dist/all.js": "/js/dist/all.js?id=8b6d5790410cef7c138c7807516eb0b0",
"/js/dist/all-defer.js": "/js/dist/all-defer.js?id=07e52318da2cdf3171c4d88113f25fb6",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=44f9320d0739f419c9246f7f39395b02",
+2 -2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"/livewire.js":"/livewire.js?id=90730a3b0e7144480175"}
{"/livewire.js":"/livewire.js?id=c69d0f2801c01fcf8166"}
+40
View File
@@ -0,0 +1,40 @@
/*
* dragtable
*
* @Version 2.0.15
*
* default css
*
*/
/*##### the dragtable stuff #####*/
.dragtable-sortable {
list-style-type: none; margin: 0; padding: 0; -moz-user-select: none;
}
.dragtable-sortable li {
margin: 0; padding: 0; float: left; font-size: 1em; background: white;
}
.dragtable-sortable th, .dragtable-sortable td{
border-left: 0px;
}
.dragtable-sortable li:first-child th, .dragtable-sortable li:first-child td {
border-left: 1px solid #CCC;
}
.ui-sortable-helper {
opacity: 0.7;filter: alpha(opacity=70);
}
.ui-sortable-placeholder {
-moz-box-shadow: 4px 5px 4px #C6C6C6 inset;
-webkit-box-shadow: 4px 5px 4px #C6C6C6 inset;
box-shadow: 4px 5px 4px #C6C6C6 inset;
border-bottom: 1px solid #CCCCCC;
border-top: 1px solid #CCCCCC;
visibility: visible !important;
background: #EFEFEF !important;
visibility: visible !important;
}
.ui-sortable-placeholder * {
opacity: 0.0; visibility: hidden;
}
File diff suppressed because it is too large Load Diff
+403
View File
@@ -0,0 +1,403 @@
/*!
* dragtable
*
* @Version 2.0.15
*
* Copyright (c) 2010-2013, Andres akottr@gmail.com
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* Inspired by the the dragtable from Dan Vanderkam (danvk.org/dragtable/)
* Thanks to the jquery and jqueryui comitters
*
* Any comment, bug report, feature-request is welcome
* Feel free to contact me.
*/
/* TOKNOW:
* For IE7 you need this css rule:
* table {
* border-collapse: collapse;
* }
* Or take a clean reset.css (see http://meyerweb.com/eric/tools/css/reset/)
*/
/* TODO: investigate
* Does not work properly with css rule:
* html {
* overflow: -moz-scrollbars-vertical;
* }
* Workaround:
* Fixing Firefox issues by scrolling down the page
* http://stackoverflow.com/questions/2451528/jquery-ui-sortable-scroll-helper-element-offset-firefox-issue
*
* var start = $.noop;
* var beforeStop = $.noop;
* if($.browser.mozilla) {
* var start = function (event, ui) {
* if( ui.helper !== undefined )
* ui.helper.css('position','absolute').css('margin-top', $(window).scrollTop() );
* }
* var beforeStop = function (event, ui) {
* if( ui.offset !== undefined )
* ui.helper.css('margin-top', 0);
* }
* }
*
* and pass this as start and stop function to the sortable initialisation
* start: start,
* beforeStop: beforeStop
*/
/*
* Special thx to all pull requests comitters
*/
(function($) {
$.widget("akottr.dragtable", {
options: {
revert: false, // smooth revert
dragHandle: '.table-handle', // handle for moving cols, if not exists the whole 'th' is the handle
maxMovingRows: 40, // 1 -> only header. 40 row should be enough, the rest is usually not in the viewport
excludeFooter: false, // excludes the footer row(s) while moving other columns. Make sense if there is a footer with a colspan. */
onlyHeaderThreshold: 100, // TODO: not implemented yet, switch automatically between entire col moving / only header moving
dragaccept: null, // draggable cols -> default all
persistState: null, // url or function -> plug in your custom persistState function right here. function call is persistState(originalTable)
restoreState: null, // JSON-Object or function: some kind of experimental aka Quick-Hack TODO: do it better
exact: true, // removes pixels, so that the overlay table width fits exactly the original table width
clickDelay: 10, // ms to wait before rendering sortable list and delegating click event
containment: null, // @see http://api.jqueryui.com/sortable/#option-containment, use it if you want to move in 2 dimesnions (together with axis: null)
cursor: 'move', // @see http://api.jqueryui.com/sortable/#option-cursor
cursorAt: false, // @see http://api.jqueryui.com/sortable/#option-cursorAt
distance: 0, // @see http://api.jqueryui.com/sortable/#option-distance, for immediate feedback use "0"
tolerance: 'pointer', // @see http://api.jqueryui.com/sortable/#option-tolerance
axis: 'x', // @see http://api.jqueryui.com/sortable/#option-axis, Only vertical moving is allowed. Use 'x' or null. Use this in conjunction with the 'containment' setting
beforeStart: $.noop, // returning FALSE will stop the execution chain.
beforeMoving: $.noop,
beforeReorganize: $.noop,
beforeStop: $.noop
},
originalTable: {
el: null,
selectedHandle: null,
sortOrder: null,
startIndex: 0,
endIndex: 0
},
sortableTable: {
el: $(),
selectedHandle: $(),
movingRow: $()
},
persistState: function() {
var _this = this;
this.originalTable.el.find('th').each(function(i) {
if (this.id !== '') {
_this.originalTable.sortOrder[this.id] = i;
}
});
$.ajax({
url: this.options.persistState,
data: this.originalTable.sortOrder
});
},
/*
* persistObj looks like
* {'id1':'2','id3':'3','id2':'1'}
* table looks like
* | id2 | id1 | id3 |
*/
_restoreState: function(persistObj) {
for (var n in persistObj) {
this.originalTable.startIndex = $('#' + n).closest('th').prevAll().length + 1;
this.originalTable.endIndex = parseInt(persistObj[n], 10) + 1;
this._bubbleCols();
}
},
// bubble the moved col left or right
_bubbleCols: function() {
var i, j, col1, col2;
var from = this.originalTable.startIndex;
var to = this.originalTable.endIndex;
/* Find children thead and tbody.
* Only to process the immediate tr-children. Bugfix for inner tables
*/
var thtb = this.originalTable.el.children();
if (this.options.excludeFooter) {
thtb = thtb.not('tfoot');
}
if (from < to) {
for (i = from; i < to; i++) {
col1 = thtb.find('> tr > td:nth-child(' + i + ')')
.add(thtb.find('> tr > th:nth-child(' + i + ')'));
col2 = thtb.find('> tr > td:nth-child(' + (i + 1) + ')')
.add(thtb.find('> tr > th:nth-child(' + (i + 1) + ')'));
for (j = 0; j < col1.length; j++) {
swapNodes(col1[j], col2[j]);
}
}
} else {
for (i = from; i > to; i--) {
col1 = thtb.find('> tr > td:nth-child(' + i + ')')
.add(thtb.find('> tr > th:nth-child(' + i + ')'));
col2 = thtb.find('> tr > td:nth-child(' + (i - 1) + ')')
.add(thtb.find('> tr > th:nth-child(' + (i - 1) + ')'));
for (j = 0; j < col1.length; j++) {
swapNodes(col1[j], col2[j]);
}
}
}
},
_rearrangeTableBackroundProcessing: function() {
var _this = this;
return function() {
_this._bubbleCols();
_this.options.beforeStop(_this.originalTable);
_this.sortableTable.el.remove();
restoreTextSelection();
// persist state if necessary
if (_this.options.persistState !== null) {
$.isFunction(_this.options.persistState) ? _this.options.persistState(_this.originalTable) : _this.persistState();
}
};
},
_rearrangeTable: function() {
var _this = this;
return function() {
// remove handler-class -> handler is now finished
_this.originalTable.selectedHandle.removeClass('dragtable-handle-selected');
// add disabled class -> reorgorganisation starts soon
_this.sortableTable.el.sortable("disable");
_this.sortableTable.el.addClass('dragtable-disabled');
_this.options.beforeReorganize(_this.originalTable, _this.sortableTable);
// do reorganisation asynchronous
// for chrome a little bit more than 1 ms because we want to force a rerender
_this.originalTable.endIndex = _this.sortableTable.movingRow.prevAll().length + 1;
setTimeout(_this._rearrangeTableBackroundProcessing(), 50);
};
},
/*
* Disrupts the table. The original table stays the same.
* But on a layer above the original table we are constructing a list (ul > li)
* each li with a separate table representig a single col of the original table.
*/
_generateSortable: function(e) {
!e.cancelBubble && (e.cancelBubble = true);
var _this = this;
// table attributes
var attrs = this.originalTable.el[0].attributes;
var attrsString = '';
for (var i = 0; i < attrs.length; i++) {
if (attrs[i].nodeValue && attrs[i].nodeName != 'id' && attrs[i].nodeName != 'width') {
attrsString += attrs[i].nodeName + '="' + attrs[i].nodeValue + '" ';
}
}
// row attributes
var rowAttrsArr = [];
//compute height, special handling for ie needed :-(
var heightArr = [];
this.originalTable.el.find('tr').slice(0, this.options.maxMovingRows).each(function(i, v) {
// row attributes
var attrs = this.attributes;
var attrsString = "";
for (var j = 0; j < attrs.length; j++) {
if (attrs[j].nodeValue && attrs[j].nodeName != 'id') {
attrsString += " " + attrs[j].nodeName + '="' + attrs[j].nodeValue + '"';
}
}
rowAttrsArr.push(attrsString);
heightArr.push($(this).height());
});
// compute width, no special handling for ie needed :-)
var widthArr = [];
// compute total width, needed for not wrapping around after the screen ends (floating)
var totalWidth = 0;
/* Find children thead and tbody.
* Only to process the immediate tr-children. Bugfix for inner tables
*/
var thtb = _this.originalTable.el.children();
if (this.options.excludeFooter) {
thtb = thtb.not('tfoot');
}
thtb.find('> tr > th').each(function(i, v) {
var w = $(this).is(':visible') ? $(this).outerWidth() : 0;
widthArr.push(w);
totalWidth += w;
});
if(_this.options.exact) {
var difference = totalWidth - _this.originalTable.el.outerWidth();
widthArr[0] -= difference;
}
// one extra px on right and left side
totalWidth += 2
var sortableHtml = '<ul class="dragtable-sortable" style="position:absolute; width:' + totalWidth + 'px;">';
// assemble the needed html
thtb.find('> tr > th').each(function(i, v) {
var width_li = $(this).is(':visible') ? $(this).outerWidth() : 0;
sortableHtml += '<li style="width:' + width_li + 'px;">';
sortableHtml += '<table ' + attrsString + '>';
var row = thtb.find('> tr > th:nth-child(' + (i + 1) + ')');
if (_this.options.maxMovingRows > 1) {
row = row.add(thtb.find('> tr > td:nth-child(' + (i + 1) + ')').slice(0, _this.options.maxMovingRows - 1));
}
row.each(function(j) {
// TODO: May cause duplicate style-Attribute
var row_content = $(this).clone().wrap('<div></div>').parent().html();
if (row_content.toLowerCase().indexOf('<th') === 0) sortableHtml += "<thead>";
sortableHtml += '<tr ' + rowAttrsArr[j] + '" style="height:' + heightArr[j] + 'px;">';
sortableHtml += row_content;
if (row_content.toLowerCase().indexOf('<th') === 0) sortableHtml += "</thead>";
sortableHtml += '</tr>';
});
sortableHtml += '</table>';
sortableHtml += '</li>';
});
sortableHtml += '</ul>';
this.sortableTable.el = this.originalTable.el.before(sortableHtml).prev();
// set width if necessary
this.sortableTable.el.find('> li > table').each(function(i, v) {
$(this).css('width', widthArr[i] + 'px');
});
// assign this.sortableTable.selectedHandle
this.sortableTable.selectedHandle = this.sortableTable.el.find('th .dragtable-handle-selected');
var items = !this.options.dragaccept ? 'li' : 'li:has(' + this.options.dragaccept + ')';
this.sortableTable.el.sortable({
items: items,
stop: this._rearrangeTable(),
// pass thru options for sortable widget
revert: this.options.revert,
tolerance: this.options.tolerance,
containment: this.options.containment,
cursor: this.options.cursor,
cursorAt: this.options.cursorAt,
distance: this.options.distance,
axis: this.options.axis
});
// assign start index
this.originalTable.startIndex = $(e.target).closest('th').prevAll().length + 1;
this.options.beforeMoving(this.originalTable, this.sortableTable);
// Start moving by delegating the original event to the new sortable table
this.sortableTable.movingRow = this.sortableTable.el.find('> li:nth-child(' + this.originalTable.startIndex + ')');
// prevent the user from drag selecting "highlighting" surrounding page elements
disableTextSelection();
// clone the initial event and trigger the sort with it
this.sortableTable.movingRow.trigger($.extend($.Event(e.type), {
which: 1,
clientX: e.clientX,
clientY: e.clientY,
pageX: e.pageX,
pageY: e.pageY,
screenX: e.screenX,
screenY: e.screenY
}));
// Some inner divs to deliver the posibillity to style the placeholder more sophisticated
var placeholder = this.sortableTable.el.find('.ui-sortable-placeholder');
if(!placeholder.height() <= 0) {
placeholder.css('height', this.sortableTable.el.find('.ui-sortable-helper').height());
}
placeholder.html('<div class="outer" style="height:100%;"><div class="inner" style="height:100%;"></div></div>');
},
bindTo: {},
_create: function() {
this.originalTable = {
el: this.element,
selectedHandle: $(),
sortOrder: {},
startIndex: 0,
endIndex: 0
};
// bind draggable to 'th' by default
this.bindTo = this.originalTable.el.find('th');
// filter only the cols that are accepted
if (this.options.dragaccept) {
this.bindTo = this.bindTo.filter(this.options.dragaccept);
}
// bind draggable to handle if exists
if (this.bindTo.find(this.options.dragHandle).length > 0) {
this.bindTo = this.bindTo.find(this.options.dragHandle);
}
// restore state if necessary
if (this.options.restoreState !== null) {
$.isFunction(this.options.restoreState) ? this.options.restoreState(this.originalTable) : this._restoreState(this.options.restoreState);
}
var _this = this;
this.bindTo.mousedown(function(evt) {
// listen only to left mouse click
if(evt.which!==1) return;
if (_this.options.beforeStart(_this.originalTable) === false) {
return;
}
clearTimeout(this.downTimer);
this.downTimer = setTimeout(function() {
_this.originalTable.selectedHandle = $(this);
_this.originalTable.selectedHandle.addClass('dragtable-handle-selected');
_this._generateSortable(evt);
}, _this.options.clickDelay);
}).mouseup(function(evt) {
clearTimeout(this.downTimer);
});
},
redraw: function(){
this.destroy();
this._create();
},
destroy: function() {
this.bindTo.unbind('mousedown');
$.Widget.prototype.destroy.apply(this, arguments); // default destroy
// now do other stuff particular to this widget
}
});
/** closure-scoped "private" functions **/
var body_onselectstart_save = $(document.body).attr('onselectstart'),
body_unselectable_save = $(document.body).attr('unselectable');
// css properties to disable user-select on the body tag by appending a <style> tag to the <head>
// remove any current document selections
function disableTextSelection() {
// jQuery doesn't support the element.text attribute in MSIE 8
// http://stackoverflow.com/questions/2692770/style-style-textcss-appendtohead-does-not-work-in-ie
var $style = $('<style id="__dragtable_disable_text_selection__" type="text/css">body { -ms-user-select:none;-moz-user-select:-moz-none;-khtml-user-select:none;-webkit-user-select:none;user-select:none; }</style>');
$(document.head).append($style);
$(document.body).attr('onselectstart', 'return false;').attr('unselectable', 'on');
if (window.getSelection) {
window.getSelection().removeAllRanges();
} else {
document.selection.empty(); // MSIE http://msdn.microsoft.com/en-us/library/ms535869%28v=VS.85%29.aspx
}
}
// remove the <style> tag, and restore the original <body> onselectstart attribute
function restoreTextSelection() {
$('#__dragtable_disable_text_selection__').remove();
if (body_onselectstart_save) {
$(document.body).attr('onselectstart', body_onselectstart_save);
} else {
$(document.body).removeAttr('onselectstart');
}
if (body_unselectable_save) {
$(document.body).attr('unselectable', body_unselectable_save);
} else {
$(document.body).removeAttr('unselectable');
}
}
function swapNodes(a, b) {
var aparent = a.parentNode;
var asibling = a.nextSibling === b ? a : a.nextSibling;
b.parentNode.insertBefore(a, b);
aparent.insertBefore(b, asibling);
}
})(jQuery);
+8
View File
@@ -454,12 +454,18 @@ $(document).ready(function () {
$('#assigned_location').hide();
$('.notification-callout').fadeOut();
$('[name="assigned_location"]').val('').trigger('change.select2');
$('[name="assigned_user"]').val('').trigger('change.select2');
} else if (assignto_type == 'location') {
$('#current_assets_box').fadeOut();
$('#assigned_asset').hide();
$('#assigned_user').hide();
$('#assigned_location').show();
$('.notification-callout').fadeOut();
$('[name="assigned_asset"]').val('').trigger('change.select2');
$('[name="assigned_user"]').val('').trigger('change.select2');
} else {
$('#assigned_asset').hide();
@@ -470,6 +476,8 @@ $(document).ready(function () {
}
$('.notification-callout').fadeIn();
$('[name="assigned_asset"]').val('').trigger('change.select2');
$('[name="assigned_location"]').val('').trigger('change.select2');
}
});
});
+7 -1
View File
@@ -39,12 +39,18 @@ $(function () {
model = link.data("dependency");
select = link.data("select");
refreshSelector = link.data("refresh");
$('#createModal').load(link.attr('href'),function () {
// this sets the focus to be the name field
$('#modal-name').focus();
//do we need to re-select2 this, after load? Probably.
$('#createModal').find('select.select2').select2();
// Initialize the ajaxy select2 with images.
// This is a copy/paste of the code from snipeit.js, would be great to only have this in one place.
$('.js-data-ajax').each( function (i,item) {
var link = $(item);
var endpoint = link.data("endpoint");

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