Compare commits

...

367 Commits

Author SHA1 Message Date
snipe d990d753ce Switch to 660 2026-03-09 08:23:03 +00:00
snipe 425bab096d Added fix-permissions script to composer to handle #18601 2026-03-08 14:52:23 +00:00
snipe 338fc88095 Merge pull request #18647 from grokability/revert-18601-chore/security-upgrade-passport13-socialite-jwt7
Revert "Upgrade Passport to v13 and move php-jwt to v7 to remediate JWT advisory"
2026-03-08 12:32:31 +00:00
snipe 0a724cc49a Revert "Upgrade Passport to v13 and move php-jwt to v7 to remediate JWT advisory" 2026-03-08 12:29:56 +00:00
snipe a2efcf1ca9 Merge pull request #18601 from ubc-cpsc/chore/security-upgrade-passport13-socialite-jwt7
Upgrade Passport to v13 and move php-jwt to v7 to remediate JWT advisory
2026-03-08 11:51:15 +00:00
snipe c9d73e85e1 Fixed RB-4083 2026-03-08 11:37:19 +00:00
Joël Pittet a956676745 Move Passport 13 key permission normalization to upgrade.php to cover existing installs with incorrect permissions 2026-03-07 22:22:46 -08:00
Joël Pittet 5800d08202 normalize key permissions 2026-03-07 21:59:07 -08:00
Joël Pittet b8958bad72 Test failure fixes from 'Key Files Permissions Validation' from https://github.com/laravel/passport/blob/13.x/UPGRADE.md#key-files-permissions-validation 2026-03-07 21:59:00 -08:00
Joël Pittet 0508d7558e OAuth Client Table Changes (Optional) from https://github.com/laravel/passport/blob/13.x/UPGRADE.md#oauth-client-table-changes-optional 2026-03-07 21:59:00 -08:00
Joël Pittet 98c3192a13 Bump laravel/socialite to latest patch release, upgrade laravel/passport to 13 which is compatible with Laravel 11, and security release for firebase/php-jwt 2026-03-07 21:59:00 -08:00
snipe b886e11670 Fixed user API tests 2026-03-07 20:50:18 +00:00
snipe 92bb1df82e Removed error log in test 2026-03-07 11:05:33 +00:00
snipe 1c4549dd8e Added word break on history 2026-03-07 11:04:25 +00:00
snipe 83e61ec8cd Unset admin if auth user is not admin 2026-03-07 10:45:57 +00:00
snipe 7ea549df4a Added .journal to gitignore 2026-03-06 21:57:23 +00:00
snipe af1c55cd7e Small code re-org for clarity 2026-03-06 21:55:46 +00:00
snipe 7822c0bc3e Merge pull request #18644 from uberbrady/fix_location_observer
Re-add Location Observer with null-safe companyable check
2026-03-06 21:47:58 +00:00
Brady Wetherington 2b7ed1f7fa Re-add Location Observer with null-safe companyable check 2026-03-06 21:43:19 +00:00
snipe 45ddaf1b72 Temp comment out location observer 2026-03-06 20:14:56 +00:00
snipe 7595c922fa Fixed #18043 - added location logging 2026-03-06 14:03:37 +00:00
snipe 4d4513d936 Fixed #18642 - view-assets mobile 2026-03-06 13:40:01 +00:00
snipe e378c99923 Layout tweakas 2026-03-06 13:29:38 +00:00
snipe 078c870970 Merge pull request #18162 from Godmartinz/status_bulk_select_fix
Adds #17685 better warning for bulk status change to undeployable type
2026-03-06 09:58:41 +01:00
snipe 4a91d37423 Merge pull request #17666 from akemidx/xxdays-since-last-update
Feature: Added xx days since last update to Custom Report
2026-03-06 09:54:32 +01:00
snipe 96befb2aef Merge branch 'develop' into xxdays-since-last-update 2026-03-06 09:54:01 +01:00
snipe 925452e106 Merge pull request #18248 from iryadifarhan/fix/depreciation-report-displays-incorrect-monthly-depreciation-value
Fixes inaccurate monthly depreciation value displayed at depreciation report when using a floor value
2026-03-06 09:40:39 +01:00
snipe 1678330f0c Merge pull request #18426 from akemidx/global-report-templates
Fixed: Added global report templates
2026-03-06 09:30:15 +01:00
snipe 58cf3fc2ea Dev assets 2026-03-06 08:17:48 +00:00
snipe 13ce17ffc6 Merge pull request #18494 from marcusmoore/livewire4
Upgraded Livewire to v4
2026-03-06 09:16:25 +01:00
snipe 70e020ee0c Merge pull request #18637 from marcusmoore/test-updates
Fixed some minor issues in tests
2026-03-06 09:16:00 +01:00
snipe c6a167ef40 Removed min-height (this might go badly) 2026-03-06 07:42:28 +00:00
snipe 420ba5f261 Fixed #18341 - remove action button from user profile view 2026-03-06 06:45:46 +00:00
snipe 7aa1f3ae1d Merge pull request #18639 from grokability/#18282-add-user-notes-to-custom-export
Fixed #18282 - added original notes to checkout target in custom report
2026-03-06 07:23:58 +01:00
snipe 556d638136 Fixed #18282 - added original notes to checkout target in custom report 2026-03-06 06:19:16 +00:00
snipe c4aa10baf8 Added user company to custom asset report 2026-03-06 05:59:11 +00:00
snipe 86f647e714 Improved bulk audit bg 2026-03-06 05:39:09 +00:00
snipe 64f5d40fd9 Removed duplicate code 2026-03-06 05:21:59 +00:00
snipe 1a0869c2ca Merge pull request #18628 from marcusmoore/fixes/fd-54108-checkin-all-button
Fixed Checkin All Seats button
2026-03-06 06:17:12 +01:00
snipe 1fb32ee461 Merge pull request #18638 from grokability/experiments/compact-nav
Compacted nav UI, components for buttons
2026-03-06 05:45:02 +01:00
snipe 68a863e63e Updated test, fixed route 2026-03-06 04:38:48 +00:00
snipe a1e62ccd46 Fixed (shimmed) #18636 - added shim roite for fieldsets 2026-03-06 03:54:33 +00:00
snipe dae66c0fa4 Fixed audit background 2026-03-06 03:48:45 +00:00
snipe 1d6b21e1c7 Switched to newer button model 2026-03-06 03:33:04 +00:00
snipe 455be12058 Gate fix for admin 2026-03-06 03:05:40 +00:00
snipe 2ebb3ecb96 Update views with new button blades 2026-03-06 01:17:00 +00:00
Marcus Moore b29056dddf Delete duplicate test 2026-03-05 16:35:15 -08:00
Marcus Moore 3ed09f304c Fix method name 2026-03-05 16:30:05 -08:00
Marcus Moore 4875283ed1 Fix test route 2026-03-05 16:20:27 -08:00
Marcus Moore 261231c09e Fix method and variable names 2026-03-05 15:55:59 -08:00
snipe 180dad6ee6 Use updated infobox 2026-03-05 14:36:12 +00:00
snipe d7121ad82f Removed class active (this is handled in jquery) 2026-03-05 11:46:55 +00:00
snipe f121c61524 Added hidden-print class to action buttons column 2026-03-05 11:45:21 +00:00
Marcus Moore 93bf91869b Fix checkin all seats button 2026-03-03 14:57:43 -08:00
snipe 2ed32612b9 Fixed typo in comments 2026-03-02 18:35:57 +01:00
snipe 314bcbe208 Added assets upload url singleton 2026-03-02 18:35:57 +01:00
snipe 6290e34623 Exclude show route for settings API [RB-17718] 2026-03-02 18:35:57 +01:00
snipe 5e5521a128 Merge pull request #18623 from grokability/dependabot/github_actions/develop/actions/upload-artifact-7
Bump actions/upload-artifact from 6 to 7
2026-03-02 12:51:02 +00:00
dependabot[bot] 1e8bed86d3 Bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 08:43:13 +00:00
snipe 8bc7d50e35 Updated to check gate on object 2026-02-26 16:03:57 +00:00
Marcus Moore aa420e5006 Bump Livewire to v4.1.4 2026-02-25 13:30:30 -08:00
Marcus Moore 4babaa85fc Merge branch 'develop' into livewire4
# Conflicts:
#	composer.lock
2026-02-25 13:29:07 -08:00
snipe 378d9e008e Removed truncate that ran after maintenance seeder 2026-02-25 19:46:09 +00:00
snipe 340fabe4f6 Merge pull request #18614 from spencerrlongg/eof-rollbar
Fixes (hopefully) RB #19772 Unexpected EOF
2026-02-25 19:05:30 +00:00
spencerrlongg 29331b8b06 missing semicolon - kind of a shot in the dark 2026-02-25 12:54:43 -06:00
snipe 6fbcaa0959 Make sure manager_id is an integer 2026-02-25 18:41:53 +00:00
snipe 52e10205c5 Add license ID to tabke cookie 2026-02-25 18:40:29 +00:00
snipe f419ae3f4a Fixed license history target 2026-02-25 17:41:26 +00:00
snipe 84a6b8e012 Add display_name to asset advanced search filter 2026-02-25 16:54:54 +00:00
snipe f234aee691 Use display_name in transformUserCompact() 2026-02-25 16:41:30 +00:00
snipe 623d516595 Merge pull request #18095 from marcusmoore/5947-bulk-checkout-rollup
#5947 - roll up bulk asset checkout email
2026-02-25 15:36:58 +00:00
snipe 156eaf53d5 Fix for #18408 2026-02-25 15:30:37 +00:00
snipe 678a7c1437 Removed BS class 2026-02-25 15:04:24 +00:00
snipe ce2a760093 Merge pull request #17964 from Godmartinz/add-License-checkedout-to-user
Adds #11741 currently assigned license table to license checkout
2026-02-25 14:50:39 +00:00
snipe 34b02d8534 Merge pull request #18485 from marcusmoore/debugbar
Bumped Debugbar to v4
2026-02-25 14:43:03 +00:00
snipe 42e0e691dd Merge pull request #18609 from ubc-cpsc/bugfix/php-deprecated-string-interpolation
Fix deprecated string interpolation in controllers
2026-02-25 14:41:24 +00:00
snipe ccfda99060 Merge pull request #18613 from grokability/add-sorting-on-model-name-and-number-for-maintenances
Added model number as a separate field, added sorting
2026-02-25 14:22:48 +00:00
snipe 57f02bdc57 Fixed typo 2026-02-25 14:22:27 +00:00
snipe 7056656eab Added model number as a separate field, added sorting 2026-02-25 14:19:01 +00:00
snipe 2d899fc772 Merge pull request #18612 from grokability/added-maintenances-seeder
Added maintenances seeder
2026-02-25 14:16:44 +00:00
snipe 80ee0835aa Added maintenances seeder 2026-02-25 14:12:13 +00:00
snipe 7b7e6c7971 Fixed maintence image display 2026-02-25 12:39:29 +00:00
snipe 025fb30335 Added user email to exportable field in custom report 2026-02-25 12:03:23 +00:00
snipe fcace4a192 Override the table search with the asset tag search if one is passed 2026-02-25 11:50:52 +00:00
snipe 619e1c70ea Fixed #18603 - set unique cookie ID on asset history table 2026-02-25 11:48:42 +00:00
Joël Pittet 7a95bdfba6 Fix deprecated string interpolation in controllers 2026-02-24 14:57:29 -08:00
Marcus Moore 89962de161 Move to Traits directory 2026-02-24 11:37:07 -08:00
Marcus Moore d9328bd0ce Merge branch 'develop' into debugbar 2026-02-24 11:27:04 -08:00
snipe 81306df06a Merge pull request #18606 from grokability/#18600-check-filesystem-on-healthcheck
Fixed #18600 - add filesystem check on health checker
2026-02-24 14:00:14 +00:00
snipe 75abe9eb3e Re-add try/catch for DB for cleaner output 2026-02-24 13:23:12 +00:00
snipe 1f26bf1125 Fixed #18600 - add filesystem check on health checker 2026-02-24 13:17:15 +00:00
snipe 14b9c06d77 Remove optional qualifier 2026-02-24 12:00:06 +00:00
snipe 4f3d23d1ef Merge pull request #18576 from marcusmoore/fixes/rb-20713-license-seat-assigned-to
Fixed RB-20713: Improved validation of license seat update api endpoint
2026-02-24 07:29:41 +00:00
Marcus Moore cada43f34d Allow checking in from unfound targets 2026-02-23 17:23:42 -08:00
snipe 9188f89a2a Tagged v8.4.0 2026-02-23 20:40:40 +00:00
Marcus Moore 1cc7ef0543 Organization 2026-02-23 10:51:34 -08:00
snipe a86ffc29d1 Added display name to search term in advanced search 2026-02-23 14:43:55 +00:00
snipe 9ed5540c49 Merge pull request #18597 from uberbrady/improve_ldap_binding
Improve LDAP escaping to better reflect modern PHP standards
2026-02-23 13:38:28 +00:00
Brady Wetherington 3a7e00ccc3 Improve LDAP escaping to better reflect modern PHP standards 2026-02-23 13:01:00 +00:00
snipe f82cdabccd More info anel refinements 2026-02-23 11:41:45 +00:00
snipe b9c9cc0046 Merge pull request #18596 from grokability/add-with-trashed-to-formattednamelink
Adds withTrashed() to `adminuser` and updates presenter to show strikethrough
2026-02-23 11:00:43 +00:00
snipe 27ece84d52 Adds withTrashed() to adminuser and updates presenter to show strikethrough 2026-02-23 10:49:44 +00:00
snipe 1299186fb8 Merge pull request #18591 from grokability/#18017-show-more-info-in-bulk-audit
Fixed #18017 - show status on bulk audit
2026-02-21 13:17:31 +00:00
snipe d837e4845b Show note 2026-02-21 13:16:58 +00:00
snipe 39be0c5590 Fixed #18017 - show status on bulk audit 2026-02-21 13:08:23 +00:00
snipe d2bb10e96d Merge pull request #18590 from grokability/optionally-update-audit-dates
Added checkbox to determine if existing audit dates should be changed
2026-02-21 12:53:12 +00:00
snipe f139a616c7 Added checkbox to determine if existing audit dates should be changed 2026-02-21 12:49:36 +00:00
snipe d5099d973b Move copy icon to right side 2026-02-20 15:00:04 +00:00
snipe 82b18a3207 Updated requestable icon 2026-02-20 14:15:50 +00:00
snipe 10aee6bb5f Fixed tab pane name 2026-02-20 13:10:14 +00:00
snipe c1b11ab9bf Small fixes for accessories/checkedout tab 2026-02-20 13:05:31 +00:00
snipe c226a061f5 Tagged pre-release 2026-02-20 12:42:09 +00:00
snipe c0be738aef Removed unneeded return check 2026-02-20 12:13:01 +00:00
snipe e6987ec148 Early return on non-existent acceptance 2026-02-20 12:11:49 +00:00
snipe 973fa7a58b Merge pull request #18575 from spencerrlongg/encrypted-custom-fields-api
Adds field_encrypted To Fields API Response
2026-02-20 12:11:00 +00:00
snipe b6195ba3ae Default active tab fix 2026-02-20 11:56:23 +00:00
snipe d3bd213f29 Fixed typo in tooltip text 2026-02-20 09:49:06 +00:00
snipe 9502dd8bd7 Back out :class 2026-02-20 09:36:38 +00:00
Marcus Moore e81ccf4280 Add more failing assertions 2026-02-19 16:53:22 -08:00
Marcus Moore 9730f2c0f3 Add some failing assertions 2026-02-19 16:40:45 -08:00
Marcus Moore 1de34c0656 Some controller clean up 2026-02-19 16:40:34 -08:00
Marcus Moore bdce6b1387 Add test case 2026-02-19 13:51:20 -08:00
Marcus Moore ce0c2ecd8f Populate new tests 2026-02-19 13:47:52 -08:00
Marcus Moore 40f07e3d72 Scaffold addition test cases 2026-02-19 11:50:04 -08:00
snipe 3c3ccae7c9 Allow context overwrites in FCO specific tables 2026-02-19 19:14:26 +00:00
Godfrey M be211f6f86 fix apostophes 2026-02-19 09:57:29 -08:00
snipe 9789ca42f8 Highlight inventory in red if below min 2026-02-19 15:11:27 +00:00
snipe da26bc5165 Updated JS assets 2026-02-19 14:55:45 +00:00
snipe 0b67626961 Merge pull request #18580 from grokability/fixes-for-alert-menu
Quick fix for alert menu 1001 queries
2026-02-19 14:54:50 +00:00
snipe 546c8ce7d5 Quick fix for alert menu 1001 queries 2026-02-19 14:52:01 +00:00
snipe c76a888d11 Merge pull request #18579 from uberbrady/fix_npm_install_jspdf
Careful upgrade to minimize too many version changes
2026-02-19 14:25:34 +00:00
Brady Wetherington 38c495a4ac Careful upgrade to minimize too many version changes 2026-02-19 14:11:15 +00:00
snipe d0ce5e0c57 Merge pull request #18578 from grokability/#18435-fixed-currency-selection-selectbox
Fixed #18435 - correct option value for currency format selector
2026-02-19 12:05:01 +00:00
snipe 7a0e5b57db Fixed currency selector 2026-02-19 12:01:56 +00:00
snipe c31f1d2cce Nicer row selected color 2026-02-19 11:42:45 +00:00
snipe ea98ee07e5 Fixed #18577 - set url back parameter as default in form component 2026-02-19 11:30:53 +00:00
snipe a38bfada57 Merge pull request #18572 from uberbrady/fix_cross_company_created_by_in_asset_acceptance
Fixed #18571 - fix cross-company created_by in asset acceptance
2026-02-19 11:24:28 +00:00
snipe e5ff19aec4 Fixd required field color 2026-02-19 11:02:12 +00:00
Marcus Moore 504f63066f Add note 2026-02-18 16:34:22 -08:00
Marcus Moore 41b327529b Reference rollbar 2026-02-18 16:31:55 -08:00
Marcus Moore e84279c402 Implement remaining tests 2026-02-18 16:17:34 -08:00
spencerrlongg 65e6519f97 adds field_encrypted to fields api response 2026-02-18 17:53:04 -06:00
Marcus Moore dbbb3beacc Clean up 2026-02-18 15:35:57 -08:00
Marcus Moore e8a73a8de6 Organization 2026-02-18 15:29:44 -08:00
Marcus Moore 411cbc0962 Avoid overwriting created_by and timestamps 2026-02-18 15:20:28 -08:00
Marcus Moore b487c0e3e9 Scaffold additional tests 2026-02-18 15:20:18 -08:00
Marcus Moore fa0e8f6e01 Improve validation 2026-02-18 15:11:52 -08:00
Marcus Moore e25e405f74 Scaffold additional test cases and organize 2026-02-18 14:52:43 -08:00
Marcus Moore 302c4a6414 Organize test 2026-02-18 14:35:06 -08:00
Marcus Moore d1e7f6e55e Inline 2026-02-18 14:03:28 -08:00
Marcus Moore d12f0974df Add test for permission 2026-02-18 14:01:53 -08:00
snipe b751fe7903 Fixed incorrect translation string 2026-02-18 16:30:37 +00:00
snipe a6e34522eb Partially convert maintenances into new blade components 2026-02-18 16:26:13 +00:00
snipe 735e6f3471 Merge pull request #18570 from grokability/convert-consumables-to-new-blade-components
Converted consumables to new blade component
2026-02-18 16:02:32 +00:00
snipe c9f41c950a Added copy+paste, added additional fields 2026-02-18 15:57:40 +00:00
Brady Wetherington 091c710940 Make User parameter nullable 2026-02-18 15:25:05 +00:00
snipe 276f412a3c Small tweaks to info panel 2026-02-18 15:22:46 +00:00
snipe 6f6e5c847a Converted consumables to new blade component 2026-02-18 14:44:08 +00:00
snipe 6ef0274bb3 Merge pull request #18569 from grokability/updated-csvs
Small importer improvements, added larger CSV samples
2026-02-18 14:22:23 +00:00
snipe 7ef6a72ec4 Updated CSVs 2026-02-18 14:06:33 +00:00
snipe e2b8c69cf6 Use iconhelper for dynamic icon 2026-02-18 14:06:24 +00:00
snipe 1254d12d83 Tweaks to info panel 2026-02-18 14:06:14 +00:00
snipe b68642a827 Added alias strings 2026-02-18 14:04:51 +00:00
snipe c5214b976b Added string 2026-02-18 14:04:44 +00:00
snipe e611121244 Added tag color to main importer, added aliases 2026-02-18 14:04:38 +00:00
snipe 3aa7f0b7fe Added tag_color to importer 2026-02-18 14:04:19 +00:00
snipe 3d38eee71d Added/updated icons 2026-02-18 14:04:00 +00:00
snipe 90e6e309f9 Removed redundant icon helper 2026-02-18 14:03:53 +00:00
snipe 885314b87a Added large components sample CSV 2026-02-18 12:01:59 +00:00
snipe ef013a7026 Updated components sample 2026-02-18 12:01:48 +00:00
snipe 0353ade90e Trim off whitespace for headers 2026-02-18 12:01:34 +00:00
snipe d90c9282ac Updated strings 2026-02-18 11:13:52 +00:00
Marcus Moore 5055aafe1d Scaffold tests 2026-02-17 17:37:24 -08:00
Marcus Moore e68b9ea267 Validate assigned_to is integer 2026-02-17 16:51:31 -08:00
Marcus Moore 1d8ba2ec61 Add failing test 2026-02-17 16:49:32 -08:00
snipe d56970eaa3 Merge pull request #18566 from grokability/fixed-new-vs-update-on-custom-fieldset-creation
Fixed new vs update button label on custom fieldset creation, fixed breadcrumbs on new custom fieldset creation screen
2026-02-17 22:23:06 +00:00
snipe 8b3f18ec59 Better breadcrumbs for fieldset creation 2026-02-17 22:17:38 +00:00
snipe 170d20ddb5 Fixed new vs update button on custom fieldset create vs edit 2026-02-17 22:17:27 +00:00
snipe 70407dac85 Merge pull request #18565 from grokability/#18555-component-clone
Added #18555 - ability to clone components
2026-02-17 16:22:02 +00:00
snipe c2334d87de Merge pull request #18564 from uberbrady/fix_component_counts
Fixed #18557 - Tweak numRemaining to not re-query for un-checked-out components
2026-02-17 15:44:00 +00:00
Brady Wetherington 6b90b3743e Tweak numRemaining to not re-query for un-checked-out components 2026-02-17 15:27:04 +00:00
Marcus Moore 7eb536f80e Bump debugbar to v4.0.7 2026-02-11 11:37:57 -08:00
Marcus Moore d91ce88e9a Merge branch 'develop' into debugbar 2026-02-11 11:37:02 -08:00
Marcus Moore 7ada7fb327 Merge branch 'develop' into livewire4 2026-02-05 10:39:56 -08:00
Marcus Moore 9d632e39bf Merge branch 'develop' into debugbar 2026-02-05 10:33:13 -08:00
Marcus Moore 5d085469ec Bump debugbar to v4.0.6 2026-02-04 10:46:34 -08:00
Marcus Moore 7c54429c13 Bump Livewire to v4.1.2 2026-02-03 09:14:41 -08:00
Marcus Moore 8dfd04573e Bump debugbar to v4.0.5 2026-02-02 09:53:17 -08:00
Marcus Moore 1aee8679d2 Fix updating bug in oauth clients 2026-01-28 12:46:57 -08:00
Marcus Moore 8829ea9b29 Update category edit form 2026-01-27 17:08:21 -08:00
Marcus Moore 6ba99e8199 Bump livewire to v4.1.0 2026-01-27 14:03:52 -08:00
Marcus Moore b4eb603f22 Switch blur to change 2026-01-27 13:58:42 -08:00
Marcus Moore 62f9ce979d Fix js 2026-01-27 13:41:57 -08:00
Marcus Moore f5928fc707 Fix tags in importer 2026-01-27 12:33:46 -08:00
Marcus Moore f7040e3616 Remove console.log 2026-01-26 17:43:47 -08:00
Marcus Moore a70f1cc1ef Migrate request hook 2026-01-26 17:38:01 -08:00
Marcus Moore a86b738231 Update route 2026-01-26 17:13:56 -08:00
Marcus Moore 8a9ddba208 Use traditional component naming 2026-01-26 17:02:32 -08:00
Marcus Moore 4c727966e7 Migrate config 2026-01-26 16:59:08 -08:00
Marcus Moore c2ac53029a Update to v4 2026-01-26 16:58:58 -08:00
Marcus Moore 5d300d85bd Update content-type assertion 2026-01-26 14:52:53 -08:00
Marcus Moore 1dc181797b Move trait to base controller 2026-01-26 14:39:48 -08:00
Marcus Moore 30d932e2e0 Re-enable exception collector 2026-01-26 14:30:03 -08:00
Marcus Moore 9fb32d33af Migrate to v4 config 2026-01-26 14:28:18 -08:00
Marcus Moore 70baa60521 Install v4 of laravel debugbar 2026-01-26 14:02:01 -08:00
Marcus Moore 8ea2784d44 Uninstall debugbar 2026-01-26 14:01:29 -08:00
Marcus Moore 2b56d82577 Move disabling debugbar to trait 2026-01-26 14:01:21 -08:00
akemidx 5d91e0fa0a name box alignment/icons for shared or not 2026-01-12 07:48:53 -05:00
akemidx eb41247f2d changing 'shared_report_template' to 'is_shared' in DB part 2, also last commit deleted an unused request file 2026-01-08 18:54:44 -07:00
akemidx d53eafe87a changing 'shared_report_template' to 'is_shared' in DB 2026-01-08 18:52:36 -07:00
Marcus Moore 065c17002e Remove unused test cases 2026-01-08 15:18:16 -08:00
Marcus Moore 0e46a54013 Improve input alignment 2026-01-08 15:08:56 -08:00
Marcus Moore 0e35fb941b Improve assertions 2026-01-08 14:17:10 -08:00
Marcus Moore 71c9b927c6 Allow using same template name 2026-01-08 14:17:00 -08:00
akemidx e42960ea15 sharing a report template test 2026-01-07 18:49:34 -07:00
Marcus Moore 636d42a52e Make test more explicit 2026-01-07 16:42:47 -08:00
Marcus Moore 1451843f84 Implement test for viewing shared template 2026-01-07 16:39:10 -08:00
Marcus Moore f8af21306a Handle attempting to delete another user's template 2026-01-07 16:37:34 -08:00
Marcus Moore 4e874cdb1b Fix update logic 2026-01-07 16:13:22 -08:00
akemidx dbd9a844dc scaffold test cases around sharing templates 2026-01-07 16:29:32 -07:00
akemidx c5284ed195 works fully, just needs to restrict editing for non template creator 2026-01-06 17:21:22 -07:00
Marcus Moore 5cd92dd794 Remove redundant display of "Category" 2026-01-05 15:13:27 -08:00
akemidx bd98ee62e1 something something prebreak 2026-01-05 12:16:16 -07:00
akemidx 33f7a8d356 save copy 2025-12-16 21:38:13 -05:00
akemidx 64c3fe9099 notes and new direction? 2025-12-16 21:27:51 -05:00
akemidx 193fba71f3 fixing validation 2025-12-11 17:21:48 -05:00
akemidx 8a14800ef2 fixes/beginning validation 2025-12-11 03:42:10 -05:00
akemidx a46d73f562 spacing/hiding when no template loaded 2025-12-11 03:21:34 -05:00
akemidx 865392d1f7 margin 2025-12-09 14:10:32 -05:00
akemidx b880ed2371 front end clarity 2025-12-09 13:14:09 -05:00
Marcus Moore def04017e0 Improve readability in tests 2025-12-08 14:10:44 -08:00
Marcus Moore 9eeb916796 Improve clarity in test 2025-12-08 14:04:18 -08:00
Marcus Moore b06a0c5d83 Use value already computed 2025-12-08 13:59:07 -08:00
Marcus Moore 90541ba349 Use foreach instead of reduce 2025-12-08 13:57:38 -08:00
Marcus Moore 0cc346259b Use foreach instead of reduce 2025-12-08 13:51:30 -08:00
Marcus Moore 98c343b438 Improve method ordering 2025-12-08 13:48:22 -08:00
akemidx 7ee0cdc6c7 hide edit and delete for non report creators 2025-12-08 16:42:38 -05:00
akemidx 6a770832ba only creator can see share checkbox 2025-12-08 16:32:11 -05:00
akemidx ef0ff65162 scoping shared templates 2025-12-08 16:29:14 -05:00
akemidx 8c0e7e1bb3 share option saved in db column 2025-12-08 15:55:26 -05:00
Marcus Moore 8cb2ef7cac Add typehint 2025-12-08 12:17:04 -08:00
Marcus Moore 046b38e5c2 Improve method name 2025-12-08 12:10:25 -08:00
Marcus Moore d50d7fd631 Account for null value 2025-12-08 12:09:58 -08:00
Marcus Moore a34ea0804d Separate out info and prompt 2025-12-04 17:22:23 -08:00
Marcus Moore 0fbf4ce443 Move singular eula to bottom of email 2025-12-04 17:08:48 -08:00
Marcus Moore d062cc45df Add translation 2025-12-04 15:18:24 -08:00
Marcus Moore da790136ff WIP 2025-12-04 14:40:51 -08:00
Marcus Moore 134f374ada WIP 2025-12-04 14:39:37 -08:00
Marcus Moore df304a894f WIP 2025-12-04 14:38:13 -08:00
Marcus Moore 2d1d90e38c Add comment 2025-12-04 14:34:50 -08:00
Marcus Moore dcbdc6fcb8 WIP 2025-12-04 14:27:53 -08:00
Marcus Moore 5a4ef15de5 Avoid rendering rule if last item in loop 2025-12-04 14:22:13 -08:00
Marcus Moore affc4c8bd9 Styling 2025-12-04 14:07:10 -08:00
Marcus Moore bc5d6e89ba Readability 2025-12-04 14:05:22 -08:00
Marcus Moore c17e6811d2 Group categories visually 2025-12-04 14:03:11 -08:00
Marcus Moore 7f097c029a Fix indent 2025-12-04 13:52:26 -08:00
akemidx 60f5affe43 more sharing framewor. tests outlines 2025-12-04 13:48:36 -05:00
akemidx e769239213 only user can change report sharing 2025-12-04 00:39:47 -05:00
akemidx 45923a74f6 only user can change report sharing 2025-12-04 00:34:30 -05:00
Marcus Moore 7bf7a87f8a Begin to display EULAs for all categories 2025-12-03 16:59:57 -08:00
Marcus Moore 8396e27a2c Revert "Avoid showing EULA"
This reverts commit 8c89eb6650.
2025-12-03 16:16:30 -08:00
Marcus Moore 5153c68b8b Remove old todos 2025-12-03 16:15:43 -08:00
Marcus Moore 391495dd86 Remove some assertions 2025-12-02 17:41:22 -08:00
Marcus Moore 8c89eb6650 Avoid showing EULA 2025-12-02 17:37:21 -08:00
Marcus Moore 4167c6ea70 Add some translations 2025-12-02 17:36:05 -08:00
Marcus Moore ca3151ce29 Improve naming 2025-12-02 16:10:53 -08:00
akemidx cc20844eff share template bits 2025-12-02 18:34:13 -05:00
Marcus Moore d876e710e4 Be more specific in tests 2025-12-02 13:48:59 -08:00
Marcus Moore 5c1290425b Improve variable name 2025-12-02 13:28:59 -08:00
Marcus Moore 2043488c67 Cleanups 2025-12-02 12:31:04 -08:00
Marcus Moore e7e48c8f03 Cleanups 2025-12-02 12:30:16 -08:00
Marcus Moore dad650b804 Readability 2025-12-02 12:28:51 -08:00
Marcus Moore d0e73714c6 Implement test 2025-12-02 11:57:02 -08:00
Marcus Moore d8b95d3a20 Organization 2025-12-02 11:53:37 -08:00
Marcus Moore 559d8cc0db Implement test 2025-12-02 11:53:01 -08:00
Marcus Moore 7a804aa576 Implement test 2025-12-02 11:51:28 -08:00
Marcus Moore 0bca66b671 Send email if asset has checkin_email set to true 2025-12-01 16:58:02 -08:00
Marcus Moore 24e5cf8121 Improve readability 2025-12-01 16:51:09 -08:00
Marcus Moore ee7c4ce0f3 Improve assertion 2025-12-01 16:47:23 -08:00
Marcus Moore 428b511687 Send if eula is set 2025-12-01 16:41:50 -08:00
Marcus Moore 49497996d5 Fix template 2025-12-01 16:41:39 -08:00
Marcus Moore bccd65e2fc Add failing test 2025-12-01 16:33:46 -08:00
Marcus Moore f2158843ce Avoid attempting to loop over null 2025-12-01 16:25:37 -08:00
Marcus Moore 87fc4a4f22 Scaffold scenarios 2025-12-01 16:01:34 -08:00
Marcus Moore 27291f9ee9 Add todo 2025-12-01 14:23:33 -08:00
Marcus Moore cba963110e Remove unused import 2025-12-01 14:22:06 -08:00
Marcus Moore aa014e3706 Improve wording 2025-12-01 14:19:54 -08:00
akemidx 58d577f67a shared/notshared marker 2025-12-01 16:48:39 -05:00
Marcus Moore cd3678841b Fix intro line to locations 2025-12-01 13:41:23 -08:00
Marcus Moore 425e0c33df Add tests for introduction line 2025-12-01 12:55:39 -08:00
Marcus Moore 2018407782 Avoid error by pre-checking if user has email address 2025-12-01 12:04:53 -08:00
iryadifarhan a4729a7de8 Fix implemented by using existing function from Depreciable trait, hence addressing the floor value 2025-11-26 10:17:21 +07:00
Marcus Moore 54f065f42c Improve test 2025-11-19 17:11:39 -08:00
Marcus Moore 333ebb88b9 Enable sending to manager 2025-11-19 17:08:00 -08:00
Marcus Moore 53ff367473 Add failing tests 2025-11-19 16:31:48 -08:00
Marcus Moore 33a7de9448 Add custom fields to email 2025-11-19 13:31:30 -08:00
akemidx c797472bcc migration, and front end 2025-11-18 15:03:28 -05:00
Godfrey M 6faf171007 update asset_deployable translation 2025-11-10 16:58:42 -08:00
Godfrey M 9348204987 remove spacing adjustment 2025-11-05 11:22:11 -08:00
Godfrey M 776c7caaa5 get warning/success status check to display 2025-11-05 11:19:05 -08:00
Godfrey M 2216b83ca7 get warning/success status check to display 2025-11-05 11:16:35 -08:00
Marcus Moore 777872d41f Add notification group 2025-10-23 13:53:38 -07:00
Marcus Moore b85d1f184a Remove redundant test 2025-10-23 13:52:52 -07:00
Marcus Moore 02129eeddb Add try/catch 2025-10-23 13:51:53 -07:00
Marcus Moore 2612e0bbc8 Remove unused import 2025-10-23 12:43:12 -07:00
Marcus Moore 3670efacb4 Implement test 2025-10-23 12:39:36 -07:00
Marcus Moore 476611b70f Remove redundant test 2025-10-23 12:27:20 -07:00
Marcus Moore 60df2a17f8 Check context when sending to alert address 2025-10-22 16:41:36 -07:00
Marcus Moore f64f4795c1 Send request instead of firing event 2025-10-22 16:39:34 -07:00
Marcus Moore e036f756d5 Improve setup 2025-10-22 16:24:47 -07:00
Marcus Moore 92fd121cae Clean up 2025-10-22 16:12:35 -07:00
Marcus Moore 6307337892 Add scenario 2025-10-22 16:09:26 -07:00
Marcus Moore fc2e35cd32 Improve assertions 2025-10-22 14:23:07 -07:00
Marcus Moore 1811e061aa Populate scenario 2025-10-22 14:21:24 -07:00
Marcus Moore 59037f0d83 Move scenario 2025-10-22 14:18:45 -07:00
Marcus Moore 67edb7d396 Send to alert email 2025-10-22 14:17:32 -07:00
Marcus Moore 0da393f950 Populate scenario 2025-10-22 14:02:16 -07:00
Marcus Moore abd30e551e Clean up 2025-10-22 13:46:19 -07:00
Marcus Moore 6fb2889a92 Clean up 2025-10-22 13:45:00 -07:00
Marcus Moore 54125d27e0 Add scenario 2025-10-22 13:44:14 -07:00
Marcus Moore 41efda5f82 Add todos 2025-10-21 13:22:41 -07:00
Marcus Moore 2aee14a800 Only send mail to target if they have an email address 2025-10-21 13:14:50 -07:00
Marcus Moore be69da0a0d Add test case 2025-10-21 13:13:45 -07:00
Marcus Moore 31a247b55b Add test case 2025-10-21 12:44:50 -07:00
Marcus Moore 33c156be16 Add failing test 2025-10-21 12:43:23 -07:00
Marcus Moore fd66a083d6 Fix assertion 2025-10-21 12:41:51 -07:00
Marcus Moore d276f50fdf Fix assertion 2025-10-21 12:30:27 -07:00
Marcus Moore 4cb748e124 Improve test assertions 2025-10-21 12:29:25 -07:00
Marcus Moore 503e6898c3 WIP 2025-10-20 16:53:36 -07:00
Marcus Moore f34056fe2e Scaffold some testing changes 2025-10-20 16:35:27 -07:00
Marcus Moore 8ff3575442 Add test for listener registration 2025-10-20 16:31:52 -07:00
Marcus Moore b5e3358bbd Add todos 2025-10-20 14:29:05 -07:00
Marcus Moore 047a1197be Add failing conditions 2025-10-20 14:18:11 -07:00
Marcus Moore 0e87843446 WIP: start testing 2025-10-16 14:30:48 -07:00
Marcus Moore 4f1ff328ad Display eula if it is the same for all items 2025-10-14 17:10:57 -07:00
Marcus Moore ad5bbb9b37 Add divider 2025-10-14 16:34:18 -07:00
Marcus Moore c6e2fd2cab Add note 2025-10-14 16:33:37 -07:00
Marcus Moore d3a7e25b86 Move expected checking and only show once 2025-10-14 16:29:59 -07:00
Marcus Moore 062445a48e Add expected checkin 2025-10-14 16:25:32 -07:00
Marcus Moore 3c32be6181 Make introduction line dynamic 2025-10-14 16:22:19 -07:00
Marcus Moore e2f4a9bf9f Make subject dynamic 2025-10-14 16:20:01 -07:00
Marcus Moore 2db4c1b2e4 Add todo 2025-10-14 16:19:42 -07:00
Marcus Moore 6ed93f4a4f Add asset details 2025-10-14 14:02:21 -07:00
Marcus Moore 9dcee71baf wip 2025-10-08 16:18:54 -07:00
Marcus Moore 9bdd0d1d1e Add admin name 2025-10-08 16:05:55 -07:00
Marcus Moore 13b51d8608 Make acceptance section dynamic 2025-10-08 15:46:36 -07:00
Marcus Moore 19969fee39 Update closing 2025-10-08 15:32:38 -07:00
Marcus Moore 28dc4bf52e Move template to correct directory 2025-10-08 14:13:04 -07:00
Marcus Moore 9a380ac3d4 Extract intro text 2025-10-08 14:12:35 -07:00
Marcus Moore 17a26b43f0 Naively send email 2025-10-08 14:01:07 -07:00
Marcus Moore 3c42acebf0 Scaffold new listener 2025-09-30 15:11:12 -07:00
Marcus Moore 3327b2ce3c Add assertions 2025-09-30 13:03:24 -07:00
Godfrey M 8c56aa9575 changed the wrong language file 2025-09-30 12:42:46 -07:00
Godfrey M d528126f15 right hand table loading, need styling and translations 2025-09-30 12:31:44 -07:00
akemidx 64d0e3928c clean erroneous added space 2025-09-30 13:11:38 -04:00
Marcus Moore 7017a0cae1 WIP: introduce event 2025-09-25 16:23:43 -07:00
Marcus Moore a40e4d7d04 Begin experimenting with context 2025-09-25 15:43:04 -07:00
Marcus Moore 9e3b56f4bc Add failing test 2025-09-25 15:41:37 -07:00
akemidx 3492ed54de i'm dumb, fixed query 2025-09-10 19:05:28 -04:00
akemidx 1455281957 updated to outside window 2025-09-10 18:49:04 -04:00
akemidx e1de57384e field for number choice 2025-08-26 18:03:47 -04:00
akemidx c95462328a selected value(s) 2025-08-19 18:35:07 -04:00
akemidx 84fc89250a backend works, no save yet 2025-08-19 14:30:26 -04:00
akemidx a80f52cbf4 putting dropdown back 2025-08-18 19:47:26 -04:00
akemidx 7ce324a3a5 controller 2025-08-14 17:42:19 -04:00
akemidx 7624082b29 dropdown 2025-08-13 18:07:39 -04:00
akemidx 6d3bba696a first front end bit 2025-08-12 19:14:11 -04:00
1088 changed files with 65156 additions and 10738 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+1 -1
View File
@@ -81,7 +81,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
@@ -0,0 +1,24 @@
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
class CheckoutablesCheckedOutInBulk
{
use Dispatchable, SerializesModels;
public function __construct(
public Collection $assets,
public Model $target,
public User $admin,
public string $checkout_at,
public string $expected_checkin,
public string $note,
) {
}
}
+2
View File
@@ -162,6 +162,8 @@ class Handler extends ExceptionHandler
$route = 'licenses.index';
} elseif (($route === 'customfieldsets.index') || ($route === 'customfields.index')) {
$route = 'fields.index';
} elseif ($route == 'actionlogs.index') {
$route = 'home';
}
return redirect()
+3 -40
View File
@@ -775,11 +775,11 @@ class Helper
public static function checkLowInventory()
{
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
$consumables = Consumable::withCount('consumableAssignments as consumables_users_count')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
$components = Component::whereNotNull('min_amt')->get();
$components = Component::withCount('assets as sum_unconstrained_assets')->whereNotNull('min_amt')->get();
$asset_models = AssetModel::where('min_amt', '>', 0)->withCount(['availableAssets', 'assets'])->get();
$licenses = License::where('min_amt', '>', 0)->get();
$licenses = License::withCount('availCount as licenses_available')->where('min_amt', '>', 0)->get();
$items_array = [];
$all_count = 0;
@@ -1404,43 +1404,6 @@ class Helper
}
/**
* Generic helper (largely used by livewire right now) that returns the font-awesome icon
* for the object type.
*
* @author A. Gianotto <snipe@snipe.net>
* @since 6.1.0
*
* @return string
*/
public static function iconTypeByItem($item) {
switch ($item) {
case 'asset':
return 'fas fa-barcode';
case 'accessory':
return 'fas fa-keyboard';
case 'component':
return 'fas fa-hdd';
case 'consumable':
return 'fas fa-tint';
case 'license':
return 'far fa-save';
case 'location':
return 'fas fa-map-marker-alt';
case 'user':
return 'fas fa-user';
case 'supplier':
return 'fa-solid fa-store';
case 'manufacturer':
return 'fa-solid fa-building';
case 'category':
return 'fa-solid fa-table-columns';
}
}
/*
* This is a shorter way to see if the app is in demo mode.
*
+9 -1
View File
@@ -12,6 +12,7 @@ class IconHelper
case 'checkin':
return 'fa-solid fa-rotate-right';
case 'edit':
case 'update':
return 'fas fa-pencil-alt';
case 'clone':
return 'far fa-clone';
@@ -36,6 +37,8 @@ class IconHelper
return 'fa-solid fa-user';
case 'users':
return 'fas fa-users';
case 'supplier':
return 'fa-solid fa-store';
case 'restore':
return 'fa-solid fa-trash-arrow-up';
case 'external-link':
@@ -87,8 +90,11 @@ class IconHelper
case 'licenses':
case 'license':
return 'far fa-save';
case 'requests':
case 'requestable':
return 'fas fa-laptop';
case 'request':
case 'requested':
return 'fa-solid fa-bell-concierge';
case 'reports':
return 'fas fa-chart-bar';
case 'heart':
@@ -254,6 +260,8 @@ class IconHelper
case 'min-qty':
return 'fa-solid fa-chart-pie';
}
}
}
@@ -74,16 +74,16 @@ class AcceptanceController extends Controller
*/
public function store(Request $request, $id) : RedirectResponse
{
$acceptance = CheckoutAcceptance::find($id);
if (!$acceptance = CheckoutAcceptance::find($id)) {
return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$assigned_user = User::find($acceptance->assigned_to_id);
$settings = Setting::getSettings();
$sig_filename='';
if (is_null($acceptance)) {
return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist'));
}
if (! $acceptance->isPending()) {
return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted'));
}
@@ -1148,7 +1148,9 @@ class AssetsController extends Controller
$payload = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
'note' => $request->input('note'),
'note' => e($request->input('note')),
'status_label' => e($asset->assetstatus->display_name),
'status_type' => $asset->assetstatus->getStatuslabelType(),
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
];
@@ -98,24 +98,52 @@ class LicenseSeatsController extends Controller
*/
public function update(Request $request, $licenseId, $seatId) : JsonResponse | array
{
$validated = $this->validate($request, [
'assigned_to' => [
'sometimes',
'int',
'nullable',
'prohibits:asset_id',
// must be a valid user or null to unassign
function ($attribute, $value, $fail) {
if (!is_null($value) && !User::where('id', $value)->whereNull('deleted_at')->exists()) {
$fail('The selected assigned_to is invalid.');
}
},
],
'asset_id' => [
'sometimes',
'int',
'nullable',
'prohibits:assigned_to',
// must be a valid asset or null to unassign
function ($attribute, $value, $fail) {
if (!is_null($value) && !Asset::where('id', $value)->whereNull('deleted_at')->exists()) {
$fail('The selected asset_id is invalid.');
}
},
],
'notes' => 'sometimes|string|nullable',
]);
$this->authorize('checkout', License::class);
$licenseSeat = LicenseSeat::with(['license', 'asset', 'user'])->find($seatId);
if (! $licenseSeat = LicenseSeat::find($seatId)) {
if (!$licenseSeat) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
}
$license = $licenseSeat->license()->first();
$license = $licenseSeat->license;
if (!$license || $license->id != intval($licenseId)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
}
$oldUser = $licenseSeat->user()->first();
$oldAsset = $licenseSeat->asset()->first();
$oldUser = $licenseSeat->user;
$oldAsset = $licenseSeat->asset;
// attempt to update the license seat
$licenseSeat->fill($request->all());
$licenseSeat->created_by = auth()->id();
$licenseSeat->fill($validated);
// check if this update is a checkin operation
// 1. are relevant fields touched at all?
@@ -140,12 +168,17 @@ class LicenseSeatsController extends Controller
if ($licenseSeat->isDirty('assigned_to')) {
$target = $is_checkin ? $oldUser : User::find($licenseSeat->assigned_to);
}
if ($licenseSeat->isDirty('asset_id')) {
$target = $is_checkin ? $oldAsset : Asset::find($licenseSeat->asset_id);
}
if ($assignmentTouched && is_null($target)){
return response()->json(Helper::formatStandardApiResponse('error', null, 'Target not found'));
// if both asset_id and assigned_to are null then we are "checking-in"
// a related model that does not exist (possible purged or bad data).
if (!is_null($request->input('asset_id')) || !is_null($request->input('assigned_to'))) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'Target not found'));
}
}
if ($licenseSeat->save()) {
@@ -155,6 +188,7 @@ class LicenseSeatsController extends Controller
$licenseSeat->unreassignable_seat = true;
$licenseSeat->save();
}
// todo: skip if target is null?
$licenseSeat->logCheckin($target, $licenseSeat->notes);
} else {
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
@@ -82,6 +82,8 @@ class MaintenancesController extends Controller
'location',
'is_warranty',
'status_label',
'model',
'model_number',
];
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
@@ -100,6 +102,12 @@ class MaintenancesController extends Controller
case 'asset_name':
$maintenances = $maintenances->OrderByAssetName($order);
break;
case 'model':
$maintenances = $maintenances->OrderByAssetModelName($order);
break;
case 'model_number':
$maintenances = $maintenances->OrderByAssetModelNumber($order);
break;
case 'serial':
$maintenances = $maintenances->OrderByAssetSerial($order);
break;
@@ -188,7 +188,7 @@ class UploadedFilesController extends Controller
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::withTrashed()->find($id);
$this->authorize('update', self::$map_object_type[$object_type]);
$this->authorize('update', $object);
if (!$object) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_upload_status.invalid_object')));
+45 -17
View File
@@ -450,10 +450,18 @@ class UsersController extends Controller
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
// Strip out the superuser permission if the API user isn't a superadmin
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
if ((is_array($permissions_array)) && (array_key_exists('superuser', $permissions_array))) {
unset($permissions_array['superuser']);
}
}
if (!auth()->user()->isAdmin()) {
if ((is_array($permissions_array)) && (array_key_exists('admin', $permissions_array))) {
unset($permissions_array['admin']);
}
}
$user->permissions = $permissions_array;
}
@@ -530,8 +538,6 @@ class UsersController extends Controller
*/
public function update(SaveUserRequest $request, User $user): JsonResponse
{
$this->authorize('update', User::class);
$this->authorize('update', $user);
/**
@@ -567,11 +573,43 @@ class UsersController extends Controller
$user->activated = $request->input('activated');
}
// We need to use has() instead of filled()
// here because we need to overwrite permissions
// if someone needs to null them out
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
$orig_permissions_array = $user->decodePermissions();
// Strip out the individual superuser permission if the API user isn't a superadmin
if (!auth()->user()->isSuperUser()) {
if (is_array($orig_permissions_array)) {
if (array_key_exists('superuser', $orig_permissions_array)) {
$permissions_array['superuser'] = $orig_permissions_array['superuser'];
}
}
}
// Strip out the individual admin permission if the API user isn't an admin
if ((!auth()->user()->isAdmin()) && (!auth()->user()->isSuperUser())) {
if (is_array($orig_permissions_array)) {
if (array_key_exists('admin', $orig_permissions_array)) {
$permissions_array['admin'] = $orig_permissions_array['admin'];
}
}
}
// This is going to update the whole thing, not just what was passed
$user->permissions = $permissions_array;
}
}
// We need to use has() instead of filled()
// here because we need to overwrite permissions
// if someone needs to null them out
if ($request->filled('display_name')) {
$user->display_name = $request->input('display_name');
@@ -586,17 +624,7 @@ class UsersController extends Controller
}
if ($request->has('permissions')) {
$permissions_array = $request->input('permissions');
// Strip out the individual superuser permission if the API user isn't a superadmin
if (!auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
}
$user->permissions = $permissions_array;
}
if ($request->has('location_id')) {
// Update the location of any assets checked out to this user
@@ -2,6 +2,7 @@
namespace App\Http\Controllers\Assets;
use App\Events\CheckoutablesCheckedOutInBulk;
use App\Helpers\Helper;
use App\Http\Controllers\CheckInOutRequest;
use App\Http\Controllers\Controller;
@@ -12,6 +13,7 @@ use App\Models\Setting;
use App\View\Label;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
@@ -644,6 +646,8 @@ class BulkAssetsController extends Controller
*/
public function storeCheckout(AssetCheckoutRequest $request) : RedirectResponse | ModelNotFoundException
{
Context::add('action', 'bulk_asset_checkout');
$this->authorize('checkout', Asset::class);
try {
@@ -730,6 +734,15 @@ class BulkAssetsController extends Controller
});
if (! $errors) {
CheckoutablesCheckedOutInBulk::dispatch(
$assets,
$target,
$admin,
$checkout_at,
$expected_checkin,
e($request->get('note')),
);
// Redirect to the new asset page
return redirect()->to('hardware')->with('success', trans_choice('admin/hardware/message.multi-checkout.success', $asset_ids));
}
+3 -2
View File
@@ -32,15 +32,16 @@ use App\Models\Location;
use App\Models\Maintenance;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Traits\DisablesDebugbar;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Auth;
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use AuthorizesRequests, DisablesDebugbar, DispatchesJobs, ValidatesRequests;
static $map_object_type = [
'accessories' => Accessory::class,
+25 -7
View File
@@ -28,23 +28,41 @@ class HealthController extends BaseController
*/
public function get()
{
try {
if (DB::select('select 2 + 2')) {
return response()->json([
'status' => 'ok',
]);
$db_status = 'ok';
} else {
$db_status = 'Could not connect to database';
}
} catch (\Exception $e) {
\Log::error('Could not connect to database');
return response()->json([
'status' => 'database connection failed',
], 500);
$db_status = 'Could not connect to database';
}
if (is_writable(storage_path('logs'))) {
$filesystem_status = 'ok';
} else {
$filesystem_status = 'Could not write to storage/logs';
}
if (($filesystem_status!='ok') || ($db_status!='ok')) {
return response()->json([
'status' =>
[
'database' => $db_status,
'filesystem' => $filesystem_status,
]
], 500);
}
return response()->json([
'status' => 'ok',
]);
}
}
@@ -315,7 +315,8 @@ class LicensesController extends Controller
public function getExportLicensesCsv()
{
$this->authorize('view', License::class);
\Debugbar::disable();
$this->disableDebugbar();
$response = new StreamedResponse(function () {
// Open output stream
+1 -1
View File
@@ -38,7 +38,7 @@ class ModalController extends Controller
if (in_array($type, $allowed_types)) {
$view = view("modals.${type}");
$view = view("modals.{$type}");
if ($type == "statuslabel") {
$view->with('statuslabel_types', Helper::statusTypeList());
@@ -19,7 +19,8 @@ class ReportTemplatesController extends Controller
$report = $request->user()->reportTemplates()->create([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
'options' => $request->except(['_token', 'name', 'is_shared']),
'is_shared' => $request->has('is_shared'),
]);
session()->flash('success', trans('admin/reports/message.create.success'));
@@ -45,6 +46,12 @@ class ReportTemplatesController extends Controller
{
$this->authorize('reports.view');
if ($reportTemplate->created_by != auth()->id()) {
return redirect()
->route('report-templates.show', $reportTemplate)
->withError(trans('general.report_not_editable'));
}
return view('reports/custom', [
'customfields' => CustomField::get(),
'template' => $reportTemplate,
@@ -55,13 +62,29 @@ class ReportTemplatesController extends Controller
{
$this->authorize('reports.view');
// Ignore "options" rules since data does not come in under that key...
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
if ($reportTemplate->created_by != auth()->id()) {
return redirect()
->route('report-templates.show', $reportTemplate)
->withError(trans('general.report_not_editable'));
}
$reportTemplate->update([
'name' => $validated['name'],
'options' => $request->except(['_token', 'name']),
]);
$properties = [
'name' => $request->input('name'),
'options' => Arr::except($request->all(), ['_token', 'id', 'name', 'is_shared']),
'is_shared' => $reportTemplate->is_shared,
];
if ($reportTemplate->created_by == $request->user()->id) {
$properties['is_shared'] = $request->boolean('is_shared');
}
$reportTemplate->fill($properties);
if ($reportTemplate->isInvalid()) {
return redirect()->back()->withInput()->withErrors($reportTemplate->getErrors());
}
$reportTemplate->save();
session()->flash('success', trans('admin/reports/message.update.success'));
@@ -72,6 +95,12 @@ class ReportTemplatesController extends Controller
{
$this->authorize('reports.view');
if ($reportTemplate->creator()->isNot(auth()->user())) {
return redirect()
->route('report-templates.show', $reportTemplate)
->withError(trans('general.generic_model_not_found', ['model' => 'report template']));
}
$reportTemplate->delete();
return redirect()->route('reports/custom')
+60 -4
View File
@@ -40,6 +40,7 @@ use League\Csv\EscapeFormula;
use App\Http\Requests\CustomAssetReportRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\RedirectResponse;
use function Livewire\before;
/**
* This controller handles all actions related to Reports for
@@ -242,7 +243,8 @@ class ReportsController extends Controller
ini_set('max_execution_time', 12000);
$this->authorize('reports.view');
\Debugbar::disable();
$this->disableDebugbar();
$response = new StreamedResponse(function () {
Log::debug('Starting streamed response');
@@ -437,8 +439,8 @@ class ReportsController extends Controller
ini_set('max_execution_time', env('REPORT_TIME_LIMIT', 12000)); //12000 seconds = 200 minutes
$this->authorize('reports.view');
$this->disableDebugbar();
\Debugbar::disable();
$customfields = CustomField::get();
$response = new StreamedResponse(function () use ($customfields, $request) {
Log::debug('Starting streamed response');
@@ -452,6 +454,10 @@ class ReportsController extends Controller
$header = [];
if($request->filled('is_shared')) {
$header[] = trans('admin/reports/general.share_template');
}
if ($request->filled('id')) {
$header[] = trans('general.id');
}
@@ -549,6 +555,14 @@ class ReportsController extends Controller
$header[] = 'Username';
}
if ($request->filled('user_company')) {
$header[] = trans('admin/reports/general.custom_export.user_company');
}
if ($request->filled('email')) {
$header[] = 'Email';
}
if ($request->filled('employee_num')) {
$header[] = 'Employee No.';
}
@@ -589,6 +603,10 @@ class ReportsController extends Controller
$header[] = trans('admin/reports/general.custom_export.user_zip');
}
if ($request->filled('target_notes')) {
$header[] = trans('admin/reports/general.custom_export.target_notes');
}
if ($request->filled('status')) {
$header[] = trans('general.status');
}
@@ -646,7 +664,14 @@ class ReportsController extends Controller
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
Log::debug('Added headers: '.$executionTime);
$assets = Asset::select('assets.*')->with(
if($request->filled('is_shared')) {
//to fill with logic for the report template and NOT the assets retrieved by the query
//do we scope here or??
}
$assets = Asset::select('assets.*')->with(
'location', 'assetstatus', 'company', 'defaultLoc', 'assignedTo',
'model.category', 'model.manufacturer', 'supplier');
@@ -753,9 +778,15 @@ class ReportsController extends Controller
$assets->whereBetween('assets.updated_at', [$request->input('last_updated_start'), $request->input('last_updated_end')]);
}
if(($request->filled('last_updated_before'))){
$last_updated_window = Carbon::parse(today()->subDays($request->input('last_updated_before')));
$assets->where('assets.updated_at', '<' , $last_updated_window);
}
if ($request->filled('exclude_archived')) {
$assets->notArchived();
}
if ($request->input('deleted_assets') == 'include_deleted') {
$assets->withTrashed();
}
@@ -763,7 +794,6 @@ class ReportsController extends Controller
$assets->onlyTrashed();
}
Log::debug($assets->toSql());
$assets->orderBy('assets.id', 'ASC')->chunk(500, function ($assets) use ($handle, $customfields, $request) {
$executionTime = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
@@ -882,6 +912,23 @@ class ReportsController extends Controller
}
}
if ($request->filled('user_company')) {
if ($asset->checkedOutToUser()) {
$row[] = ($asset->assignedto->company) ? $asset->assignedto->company->display_name : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
if ($request->filled('email')) {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
$row[] = ($asset->assignedto) ? $asset->assignedto->email : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
if ($request->filled('employee_num')) {
// Only works if we're checked out to a user, not anything else.
if ($asset->checkedOutToUser()) {
@@ -963,6 +1010,15 @@ class ReportsController extends Controller
}
}
if ($request->filled('target_notes')) {
if ($asset->checkedOutToUser()) {
$row[] = ($asset->assignedto) ? $asset->assignedto->notes : '';
} else {
$row[] = ''; // Empty string if unassigned
}
}
if ($request->filled('status')) {
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
}
+13 -14
View File
@@ -403,24 +403,23 @@ class SettingsController extends Controller
return redirect()->to('admin')->with('error', trans('admin/settings/message.update.error'));
}
// Check if the audit interval has changed - if it has, we want to update ALL of the assets audit dates
if ($request->input('audit_interval') != $setting->audit_interval) {
// This could be a negative number if the user is trying to set the audit interval to a lower number than it was before
$audit_diff_months = ((int)$request->input('audit_interval') - (int)($setting->audit_interval));
// Check if the audit interval has changed - if it has, check if we should update all of the assets audit dates
if ((($request->input('audit_interval') != $setting->audit_interval)) && ($request->input('update_existing_dates') == 1)) {
// Batch update the dates. We have to use this method to avoid time limit exceeded errors on very large datasets,
// but it DOES mean this change doesn't get logged in the action logs, since it skips the observer.
// @see https://stackoverflow.com/questions/54879160/laravel-observer-not-working-on-bulk-insert
$affected = Asset::whereNotNull('next_audit_date')
->whereNull('deleted_at')
->update(
['next_audit_date' => DB::raw('DATE_ADD(next_audit_date, INTERVAL '.$audit_diff_months.' MONTH)')]
);
Log::debug($affected .' assets affected by audit interval update');
// This could be a negative number if the user is trying to set the audit interval to a lower number than it was before
$audit_diff_months = ((int)$request->input('audit_interval') - (int)($setting->audit_interval));
// Batch update the dates. We have to use this method to avoid time limit exceeded errors on very large datasets,
// but it DOES mean this change doesn't get logged in the action logs, since it skips the observer.
// @see https://stackoverflow.com/questions/54879160/laravel-observer-not-working-on-bulk-insert
$affected = Asset::whereNotNull('next_audit_date')
->whereNull('deleted_at')
->update(
['next_audit_date' => DB::raw('DATE_ADD(next_audit_date, INTERVAL ' . $audit_diff_months . ' MONTH)')]
);
Log::debug($affected . ' assets affected by audit interval update');
}
$alert_email = rtrim($request->input('alert_email'), ',');
@@ -133,7 +133,7 @@ class UploadedFilesController extends Controller
// Check the permissions to make sure the user can view the object
$object = self::$map_object_type[$object_type]::withTrashed()->find($id);
$this->authorize('update', self::$map_object_type[$object_type]);
$this->authorize('update', $object);
if (!$object) {
return redirect()->back()->withFragment('files')->with('error',trans('general.file_upload_status.invalid_object'));
+17 -3
View File
@@ -122,9 +122,22 @@ class UsersController extends Controller
// Strip out the superuser permission if the user isn't a superadmin
$permissions_array = $request->input('permission');
if (! auth()->user()->isSuperUser()) {
unset($permissions_array['superuser']);
// Strip out the individual superuser permission if the API user isn't a superadmin
if (!auth()->user()->isSuperUser()) {
if ((is_array($permissions_array)) && (array_key_exists('superuser', $permissions_array))) {
unset($permissions_array['superuser']);
}
}
// Strip out the individual admin permission if the API user isn't an admin
if (!auth()->user()->isAdmin()) {
if ((is_array($permissions_array)) && (array_key_exists('admin', $permissions_array))) {
unset($permissions_array['admin']);
}
}
$user->permissions = json_encode($permissions_array);
// we have to invoke the form request here to handle image uploads
@@ -510,7 +523,8 @@ class UsersController extends Controller
public function getExportUserCsv()
{
$this->authorize('view', User::class);
\Debugbar::disable();
$this->disableDebugbar();
$response = new StreamedResponse(function () {
// Open output stream
@@ -206,7 +206,7 @@ class ViewAssetsController extends Controller
if ($fullItemType == Asset::class) {
$data['item_url'] = route('hardware.show', $item->id);
} else {
$data['item_url'] = route("view/${itemType}", $item->id);
$data['item_url'] = route("view/{$itemType}", $item->id);
}
$settings = Setting::getSettings();
+1 -1
View File
@@ -34,7 +34,7 @@ class SaveUserRequest extends FormRequest
{
$rules = [
'department_id' => 'nullable|integer|exists:departments,id',
'manager_id' => 'nullable|exists:users,id',
'manager_id' => 'nullable|integer|exists:users,id',
'company_id' => ['nullable', 'integer', 'exists:companies,id']
];
+1 -1
View File
@@ -211,7 +211,7 @@ class AssetsTransformer
return $asset->assigned ? [
'id' => (int) $asset->assigned->id,
'username' => e($asset->assigned->username),
'name' => e($asset->assigned->getFullNameAttribute()),
'name' => e($asset->assigned->display_name),
'first_name'=> e($asset->assigned->first_name),
'last_name'=> ($asset->assigned->last_name) ? e($asset->assigned->last_name) : null,
'email'=> ($asset->assigned->email) ? e($asset->assigned->email) : null,
@@ -44,6 +44,7 @@ class CustomFieldsTransformer
'db_column_name' => e($field->db_column_name()),
'format' => e($field->format),
'field_values' => ($field->field_values) ? e($field->field_values) : null,
'field_encrypted' => $field->field_encrypted,
'field_values_array' => ($field->field_values) ? explode("\r\n", e($field->field_values)) : null,
'type' => e($field->element),
'required' => (($field->pivot) && ($field->pivot->required=='1')) ? true : false,
@@ -63,7 +63,7 @@ class DepreciationReportTransformer
*/
if (($asset->model) && ($asset->model->depreciation) && ($asset->model->depreciation->months !== 0)) {
$depreciated_value = Helper::formatCurrencyOutput($asset->getDepreciatedValue());
$monthly_depreciation =Helper::formatCurrencyOutput($asset->purchase_cost / $asset->model->depreciation->months);
$monthly_depreciation =Helper::formatCurrencyOutput($asset->getMonthlyDepreciation());
$diff = Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
}
else if($asset->model->eol !== null) {
@@ -37,7 +37,8 @@ class MaintenancesTransformer
'image' => ($assetmaintenance->image != '') ? Storage::disk('public')->url('maintenances/'.e($assetmaintenance->image)) : null,
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
'id' => (int) $assetmaintenance->asset->model->id,
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name) : null,
'model_number'=> ($assetmaintenance->asset->model->model_number) ? e($assetmaintenance->asset->model->model_number) : null,
] : null,
'status_label' => (($assetmaintenance->asset) && ($assetmaintenance->asset->assetstatus)) ? [
'id' => (int) $assetmaintenance->asset->assetstatus->id,
+1 -1
View File
@@ -141,7 +141,7 @@ class UsersTransformer
'id' => (int) $user->id,
'image' => e($user->present()->gravatar) ?? null,
'type' => 'user',
'name' => e($user->getFullNameAttribute()),
'name' => e($user->display_name),
'first_name' => e($user->first_name),
'last_name' => e($user->last_name),
'username' => e($user->username),
+1
View File
@@ -70,6 +70,7 @@ class CategoryImporter extends ItemImporter
$this->item['use_default_eula'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'use_default_eula'))) == 1) ? 1 : 0;
$this->item['require_acceptance'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'require_acceptance'))) == 1) ? 1 : 0;
$this->item['checkin_email'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'checkin_email'))) == 1) ? 1 : 0;
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
Log::debug('Item array is: ');
+1
View File
@@ -72,6 +72,7 @@ class ManufacturerImporter extends ItemImporter
$this->item['support_url'] = trim($this->findCsvMatch($row, 'support_url'));
$this->item['warranty_lookup_url'] = trim($this->findCsvMatch($row, 'warranty_lookup_url'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
Log::debug('Item array is: ');
+1
View File
@@ -76,6 +76,7 @@ class SupplierImporter extends ItemImporter
$this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
$this->item['url'] = trim($this->findCsvMatch($row, 'url'));
$this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
$this->item['tag_color'] = trim($this->findCsvMatch($row, 'tag_color'));
Log::debug('Item array is: ');
+13 -3
View File
@@ -33,6 +33,7 @@ use App\Notifications\CheckoutConsumableNotification;
use App\Notifications\CheckoutLicenseSeatNotification;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Exception;
@@ -128,7 +129,7 @@ class CheckoutableListener
->notify($this->getCheckoutNotification($event, $acceptance));
}
} catch (ClientException $e) {
$status = optional($e->getResponse()->getStatusCode());
$status = $e->getResponse()->getStatusCode();
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
@@ -231,7 +232,7 @@ class CheckoutableListener
->notify($this->getCheckinNotification($event));
}
} catch (ClientException $e) {
$status = optional($e->getResponse()->getStatusCode());
$status = $e->getResponse()->getStatusCode();
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
@@ -441,12 +442,17 @@ class CheckoutableListener
private function shouldSendCheckoutEmailToUser(Model $checkoutable): bool
{
/**
* Send an email if any of the following conditions are met:
* Send an email if we didn't get here from a bulk checkout
* and any of the following conditions are met:
* 1. The asset requires acceptance
* 2. The item has a EULA
* 3. The item should send an email at check-in/check-out
*/
if (Context::get('action') === 'bulk_asset_checkout') {
return false;
}
if ($checkoutable->requireAcceptance()) {
return true;
}
@@ -464,6 +470,10 @@ class CheckoutableListener
private function shouldSendEmailToAlertAddress($acceptance = null): bool
{
if (Context::get('action') === 'bulk_asset_checkout') {
return false;
}
$setting = Setting::getSettings();
if (!$setting) {
@@ -0,0 +1,154 @@
<?php
namespace App\Listeners;
use App\Events\CheckoutablesCheckedOutInBulk;
use App\Mail\BulkAssetCheckoutMail;
use App\Models\Asset;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class CheckoutablesCheckedOutInBulkListener
{
public function subscribe($events)
{
$events->listen(
CheckoutablesCheckedOutInBulk::class,
CheckoutablesCheckedOutInBulkListener::class
);
}
public function handle(CheckoutablesCheckedOutInBulk $event): void
{
$notifiableUser = $this->getNotifiableUser($event);
$shouldSendEmailToUser = $this->shouldSendCheckoutEmailToUser($notifiableUser, $event->assets);
$shouldSendEmailToAlertAddress = $this->shouldSendEmailToAlertAddress($event->assets);
if ($shouldSendEmailToUser && $notifiableUser) {
try {
Mail::to($notifiableUser)->send(new BulkAssetCheckoutMail(
$event->assets,
$event->target,
$event->admin,
$event->checkout_at,
$event->expected_checkin,
$event->note,
));
Log::info('BulkAssetCheckoutMail sent to checkout target');
} catch (Exception $e) {
Log::debug("Exception caught during BulkAssetCheckoutMail to target: " . $e->getMessage());
}
}
if ($shouldSendEmailToAlertAddress && Setting::getSettings()->admin_cc_email) {
try {
Mail::to(Setting::getSettings()->admin_cc_email)->send(new BulkAssetCheckoutMail(
$event->assets,
$event->target,
$event->admin,
$event->checkout_at,
$event->expected_checkin,
$event->note,
));
Log::info('BulkAssetCheckoutMail sent to admin_cc_email');
} catch (Exception $e) {
Log::debug("Exception caught during BulkAssetCheckoutMail to admin_cc_email: " . $e->getMessage());
}
}
}
private function shouldSendCheckoutEmailToUser(?User $user, Collection $assets): bool
{
if (!$user?->email) {
return false;
}
if ($this->hasAssetWithEula($assets)) {
return true;
}
if ($this->hasAssetWithCategorySettingToSendEmail($assets)) {
return true;
}
return $this->hasAssetThatRequiresAcceptance($assets);
}
private function shouldSendEmailToAlertAddress(Collection $assets): bool
{
$setting = Setting::getSettings();
if (!$setting) {
return false;
}
if ($setting->admin_cc_always) {
return true;
}
if (!$this->hasAssetThatRequiresAcceptance($assets)) {
return false;
}
return (bool) $setting->admin_cc_email;
}
private function hasAssetWithEula(Collection $assets): bool
{
foreach ($assets as $asset) {
if ($asset->getEula()) {
return true;
}
}
return false;
}
private function hasAssetWithCategorySettingToSendEmail(Collection $assets): bool
{
foreach ($assets as $asset) {
if ($asset->checkin_email()) {
return true;
}
}
return false;
}
private function hasAssetThatRequiresAcceptance(Collection $assets): bool
{
foreach ($assets as $asset) {
if ($asset->requireAcceptance()) {
return true;
}
}
return false;
}
private function getNotifiableUser(CheckoutablesCheckedOutInBulk $event): ?Model
{
$target = $event->target;
if ($target instanceof Asset) {
$target->load('assignedTo');
return $target->assignedto;
}
if ($target instanceof Location) {
return $target->manager;
}
return $target;
}
}
+41 -17
View File
@@ -150,10 +150,15 @@ class Importer extends Component
// if you got here, we didn't find a match. Try the $aliases_fields
foreach ($this->aliases_fields as $key => $alias_values) {
foreach ($alias_values as $alias_value) {
// Trim off any trailing spaces
$key = trim($key);
$header = trim($header);
if (strcasecmp($alias_value, $header) === 0) { // aLsO CaSe-INSENSitiVE!
// Make *absolutely* sure that this key actually _exists_ in this import type -
// you can trigger this by importing accessories with a 'Warranty' column (which don't exist
// in "Accessories"!)
if (array_key_exists($key, $this->columnOptions[$type])) {
$this->field_map[$i] = $key;
continue 3; // bust out of both of these loops and the surrounding one - e.g. move on to the next header
@@ -262,7 +267,7 @@ class Importer extends Component
'order_number' => trans('general.order_number'),
'purchase_cost' => trans('general.purchase_cost'),
'purchase_date' => trans('general.purchase_date'),
'quantity' => trans('general.qty'),
'qty' => trans('general.qty'),
'supplier' => trans('general.supplier'),
];
@@ -278,7 +283,7 @@ class Importer extends Component
'order_number' => trans('general.order_number'),
'purchase_cost' => trans('general.purchase_cost'),
'purchase_date' => trans('general.purchase_date'),
'quantity' => trans('general.qty'),
'qty' => trans('general.qty'),
'serial' => trans('general.serial_number'),
'supplier' => trans('general.supplier'),
];
@@ -372,12 +377,14 @@ class Importer extends Component
'city' => trans('general.city'),
'notes' => trans('general.notes'),
'state' => trans('general.state'),
'country' => trans('general.country'),
'zip' => trans('general.zip'),
'phone' => trans('general.phone'),
'fax' => trans('general.fax'),
'url' => trans('general.url'),
'contact' => trans('general.contact'),
'email' => trans('general.email'),
'tag_color' => trans('general.tag_color'),
];
$this->manufacturers_fields = [
@@ -389,6 +396,7 @@ class Importer extends Component
'support_email' => trans('admin/manufacturers/table.support_email'),
'warranty_lookup_url' => trans('admin/manufacturers/table.warranty_lookup_url'),
'url' => trans('general.url'),
'tag_color' => trans('general.tag_color'),
];
$this->categories_fields = [
@@ -400,6 +408,8 @@ class Importer extends Component
'use_default_eula' => trans('admin/categories/general.use_default_eula_column'),
'require_acceptance' => trans('admin/categories/general.import_require_acceptance'),
'checkin_email' => trans('admin/categories/general.import_checkin_email'),
'alert_on_response' => trans('admin/categories/general.import_alert_on_response'),
'tag_color' => trans('general.tag_color'),
];
@@ -409,16 +419,25 @@ class Importer extends Component
'category' => trans('general.category'),
'eol' => trans('general.eol'),
'fieldset' => trans('admin/models/general.fieldset'),
'name' => trans('general.item_name_var', ['item' => trans('general.asset_model')]),
'name' => trans('general.name'),
'manufacturer' => trans('general.manufacturer'),
'min_amt' => trans('mail.min_QTY'),
'model_number' => trans('general.model_no'),
'notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]),
'requestable' => trans('admin/models/general.requestable'),
'notes' => trans('general.notes'),
'requestable' => trans('general.requestable'),
'require_serial' => trans('admin/hardware/general.require_serial'),
'tag_color' => trans('general.tag_color'),
'depreciation' => trans('general.depreciation'),
];
// "real fieldnames" to a list of aliases for that field
/**
* These are the "real fieldnames" with a list of possible aliases,
* like misspellings, slight mis-phrasings, user-specific language, etc. that
* could be in the imported file header.
* This just makes the user's experience a little better when they're using
* their own CSV template.
*/
$this->aliases_fields = [
'item_name' =>
[
@@ -439,6 +458,20 @@ class Importer extends Component
'item no.',
'item #',
],
'order_number' => [
'order #',
'order no.',
'order num',
'order number',
'order',
],
'eula_text' => [
'eula',
],
'checkin_email' => [
'checkin email',
],
'asset_model' =>
[
'model name',
@@ -456,16 +489,6 @@ class Importer extends Component
'EOL',
'eol months',
],
'depreciation' =>
[
'Depreciation',
'depreciation',
],
'requestable' =>
[
'requestable',
'Requestable',
],
'gravatar' =>
[
'gravatar',
@@ -542,7 +565,8 @@ class Importer extends Component
],
'require_serial' =>
[
'serial required',
trans('admin/models/general.importer.require_serial'),
trans('admin/models/general.importer.serial_reqiured'),
],
'model_number' =>
[
+1 -1
View File
@@ -84,7 +84,7 @@ class OauthClients extends Component
]);
$client = app(ClientRepository::class)->find($editClientId->id);
if ($client->created_by == auth()->id()) {
if ($client->user_id == auth()->id()) {
$client->name = $this->editName;
$client->redirect = $this->editRedirect;
$client->save();
+165
View File
@@ -0,0 +1,165 @@
<?php
namespace App\Mail;
use App\Models\Asset;
use App\Models\CustomField;
use App\Models\Location;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
class BulkAssetCheckoutMail extends Mailable
{
use Queueable, SerializesModels;
public bool $requires_acceptance;
public Collection $assetsByCategory;
public function __construct(
public Collection $assets,
public Model $target,
public User $admin,
public string $checkout_at,
public string $expected_checkin,
public string $note,
) {
$this->requires_acceptance = $this->requiresAcceptance();
$this->loadCustomFieldsOnAssets();
$this->loadEulasOnAssets();
$this->assetsByCategory = $this->groupAssetsByCategory();
}
public function envelope(): Envelope
{
return new Envelope(
subject: $this->getSubject(),
);
}
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.bulk-asset-checkout-mail',
with: [
'introduction' => $this->getIntroduction(),
'requires_acceptance' => $this->requires_acceptance,
'requires_acceptance_info' => $this->getRequiresAcceptanceInfo(),
'requires_acceptance_prompt' => $this->getRequiresAcceptancePrompt(),
'singular_eula' => $this->getSingularEula(),
],
);
}
public function attachments(): array
{
return [];
}
private function getSubject(): string
{
if ($this->assets->count() > 1) {
return ucfirst(trans('general.assets_checked_out_count'));
}
return trans('mail.Asset_Checkout_Notification', ['tag' => $this->assets->first()->asset_tag]);
}
private function loadCustomFieldsOnAssets(): void
{
$this->assets = $this->assets->map(function (Asset $asset) {
$fields = $asset->model?->fieldset?->fields->filter(function (CustomField $field) {
return $field->show_in_email && !$field->field_encrypted;
});
$asset->setRelation('fields', $fields);
return $asset;
});
}
private function loadEulasOnAssets(): void
{
$this->assets = $this->assets->map(function (Asset $asset) {
$asset->eula = $asset->getEula();
return $asset;
});
}
private function groupAssetsByCategory(): Collection
{
return $this->assets->groupBy(fn($asset) => $asset->model->category->id);
}
private function getIntroduction(): string
{
if ($this->target instanceof Location) {
return trans_choice('mail.new_item_checked_location', $this->assets->count(), ['location' => $this->target->name]);
}
return trans_choice('mail.new_item_checked', $this->assets->count());
}
private function getRequiresAcceptanceInfo(): ?string
{
if (!$this->requires_acceptance) {
return null;
}
return trans_choice('mail.items_checked_out_require_acceptance', $this->assets->count());
}
private function getRequiresAcceptancePrompt(): ?string
{
if (!$this->requires_acceptance) {
return null;
}
$acceptanceUrl = $this->assets->count() === 1
? route('account.accept.item', $this->assets->first())
: route('account.accept');
return
sprintf(
'**[✔ %s](%s)**',
trans_choice('mail.click_here_to_review_terms_and_accept_item', $this->assets->count()),
$acceptanceUrl,
);
}
private function getSingularEula()
{
// get unique categories from all assets
$categories = $this->assets->pluck('model.category.id')->unique();
// if assets do not have the same category then return early...
if ($categories->count() > 1) {
return null;
}
// if assets do have the same category then return the shared EULA
if ($categories->count() === 1) {
return $this->assets->first()->getEula();
}
}
private function requiresAcceptance(): bool
{
foreach ($this->assets as $asset) {
if ($asset->requireAcceptance()) {
return true;
}
}
return false;
}
}
+1 -2
View File
@@ -12,7 +12,6 @@ use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Queue\SerializesModels;
class CheckoutAssetMail extends BaseMailable
@@ -25,7 +24,7 @@ class CheckoutAssetMail extends BaseMailable
* Create a new message instance.
* @throws \Exception
*/
public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
public function __construct(Asset $asset, $checkedOutTo, ?User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $asset;
$this->admin = $checkedOutBy;
+10 -9
View File
@@ -2,7 +2,6 @@
namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
@@ -21,21 +20,23 @@ use Watson\Validating\ValidatingTrait;
*/
class Accessory extends SnipeModel
{
use HasFactory;
protected $presenter = \App\Presenters\AccessoryPresenter::class;
use Acceptable;
use CompanyableTrait;
use HasFactory;
use HasUploads;
use Loggable, Presentable;
use Loggable;
use Presentable;
use Searchable;
use SoftDeletes;
use ValidatingTrait;
protected $table = 'accessories';
protected $casts = [
'purchase_date' => 'datetime',
'requestable' => 'boolean', ];
use Searchable;
use Acceptable;
protected $presenter = \App\Presenters\AccessoryPresenter::class;
/**
* The attributes that should be included when searching the model.
@@ -80,7 +81,7 @@ class Accessory extends SnipeModel
* @var bool
*/
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
/**
* The attributes that are mass assignable.
@@ -271,7 +272,7 @@ class Accessory extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -58,7 +58,7 @@ class AccessoryCheckout extends Model
*/
public function adminuser()
{
return $this->belongsTo(User::class, 'created_by');
return $this->belongsTo(User::class, 'created_by')->withTrashed();
}
/**
+3 -1
View File
@@ -907,7 +907,7 @@ class Asset extends Depreciable
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
@@ -1271,6 +1271,7 @@ class Asset extends Depreciable
$query = $query
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.display_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
@@ -1915,6 +1916,7 @@ class Asset extends Depreciable
function ($query) use ($search_val) {
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
->orWhere('users.display_name', 'LIKE', '%'.$search_val.'%')
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
}
);
+1 -1
View File
@@ -242,7 +242,7 @@ class Category extends SnipeModel
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -320,7 +320,7 @@ final class Company extends SnipeModel
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
+12 -5
View File
@@ -167,7 +167,7 @@ class Component extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
@@ -264,14 +264,21 @@ class Component extends SnipeModel
// In case there are elements checked out to assets that belong to a different company
// than this asset and full multiple company support is on we'll remove the global scope,
// so they are included in the count.
if (is_null($this->sum_unconstrained_assets) || $recalculate) {
// This, in a components-listing context, is mostly important for when it sets a 'zero' which
// is *not* null - so we don't have to keep recalculating for un-checked-out components
// the 'sum' query returns NULL when there are zero checkouts - which can inadvertently re-trigger the following query
// for un-checked-out components. So we have to do this very careful process of fetching the 'attributes'
// of the component, then see if sum_unconstrained_assets exists as an attribute. If it doesn't, we run the
// query. But if it *does* exist as an attribute - even a null - we skip the query, because that means that this
// component was fetched using withCount() - and that count *is* accurate, even if null. We just do a quick
// null-coalesce at the end to zero for the null case.
$raw_attributes = $this->getAttributes();
if (!array_key_exists('sum_unconstrained_assets', $raw_attributes) || $recalculate) {
// This part should *only* run if the component was fetched *without* withCount() (or you've asked to recalculate)
// NOTE: doing this will add a 'pseudo-attribute' to the component in question, so we need to _remove_ this
// before we save - so that gets handled in the 'saving' callback defined in the 'booted' method, above.
$this->sum_unconstrained_assets = $this->unconstrainedAssets()->sum('assigned_qty') ?? 0;
}
return $this->sum_unconstrained_assets;
return $this->sum_unconstrained_assets ?? 0;
}
+9 -1
View File
@@ -13,6 +13,7 @@ use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
@@ -131,6 +132,13 @@ class Consumable extends SnipeModel
$this->attributes['requestable'] = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
public function isDeletable()
{
return Gate::allows('delete', $this)
&& ($this->numCheckedOut() === 0)
&& ($this->deleted_at == '');
}
/**
* Establishes the consumable -> admin user relationship
*
@@ -140,7 +148,7 @@ class Consumable extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(User::class, 'created_by');
return $this->belongsTo(User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -29,6 +29,6 @@ class ConsumableAssignment extends Model
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
}
+1 -1
View File
@@ -108,7 +108,7 @@ class Depreciation extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
+1 -1
View File
@@ -68,7 +68,7 @@ class Group extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -24,6 +24,6 @@ class Import extends Model
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
}
+2 -2
View File
@@ -195,7 +195,7 @@ class Ldap extends Model
$connection = self::connectToLdap();
$ldap_username_field = $settings->ldap_username_field;
$baseDn = $settings->ldap_basedn;
$userDn = $ldap_username_field.'='.$username.','.$settings->ldap_basedn;
$userDn = $ldap_username_field . '=' . ldap_escape($username, '', LDAP_ESCAPE_DN) . ',' . $settings->ldap_basedn;
if ($settings->is_ad == '1') {
// Check if they are using the userprincipalname for the username field.
@@ -213,7 +213,7 @@ class Ldap extends Model
}
}
$filterQuery = $settings->ldap_auth_filter_query.$username;
$filterQuery = $settings->ldap_auth_filter_query . ldap_escape($username, '', LDAP_ESCAPE_FILTER);
$filter = Setting::getSettings()->ldap_filter; //FIXME - this *does* respect the ldap filter, but I believe that AdLdap2 did *not*.
$filterQuery = "({$filter}({$filterQuery}))";
+29 -3
View File
@@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Session;
use Watson\Validating\ValidatingTrait;
@@ -158,6 +159,14 @@ class License extends Depreciable
);
}
public function isDeletable()
{
return Gate::allows('delete', $this)
&& ($this->free_seats_count == $this->seats)
&& ($this->deleted_at == '');
}
protected function terminatesFormattedDate(): Attribute
{
@@ -389,7 +398,7 @@ class License extends Depreciable
*/
public function category()
{
return $this->belongsTo(\App\Models\Category::class, 'category_id');
return $this->belongsTo(\App\Models\Category::class, 'category_id')->withTrashed();
}
/**
@@ -401,7 +410,7 @@ class License extends Depreciable
*/
public function manufacturer()
{
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id');
return $this->belongsTo(\App\Models\Manufacturer::class, 'manufacturer_id')->withTrashed();
}
/**
@@ -478,7 +487,7 @@ class License extends Depreciable
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
@@ -582,6 +591,23 @@ class License extends Depreciable
->whereNull('deleted_at');
}
/**
* This is really dumb - needs to be refactored, since we have ~3 diff methods that do almost the same thing
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v2.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function numRemaining()
{
return $this->licenseSeatsRelation()
->whereNull('asset_id')
->whereNull('assigned_to')
->where('unreassignable_seat', '=', false)
->whereNull('deleted_at')
->count();
}
/**
* Sets the available seats attribute
*
+24 -10
View File
@@ -15,14 +15,18 @@ use Watson\Validating\ValidatingTrait;
class Location extends SnipeModel
{
use HasFactory;
use CompanyableTrait;
use HasFactory;
use HasUploads;
use Loggable;
use Presentable;
use Searchable;
use SoftDeletes;
use UniqueUndeletedTrait;
use ValidatingTrait;
protected $presenter = \App\Presenters\LocationPresenter::class;
use Presentable;
use SoftDeletes;
use HasUploads;
protected $table = 'locations';
protected $rules = [
@@ -53,8 +57,7 @@ class Location extends SnipeModel
* @var bool
*/
protected $injectUniqueIdentifier = true;
use ValidatingTrait;
use UniqueUndeletedTrait;
/**
* The attributes that are mass assignable.
@@ -80,16 +83,28 @@ class Location extends SnipeModel
'tag_color',
'notes',
];
protected $hidden = ['user_id'];
use Searchable;
/**
* The attributes that should be included when searching the model.
*
* @var array
*/
protected $searchableAttributes = ['name', 'address', 'city', 'state', 'zip', 'created_at', 'ldap_ou', 'phone', 'fax', 'notes'];
protected $searchableAttributes =
[
'name',
'address',
'city',
'state',
'zip',
'created_at',
'ldap_ou',
'phone',
'fax',
'notes'
];
/**
* The relations and their attributes that should be included when searching the model.
@@ -114,7 +129,6 @@ class Location extends SnipeModel
*/
public function isDeletable()
{
return Gate::allows('delete', $this)
&& ($this->deleted_at == '')
&& (($this->assets_count ?? $this->assets()->count()) === 0)
@@ -149,7 +163,7 @@ class Location extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+23 -1
View File
@@ -10,7 +10,9 @@ use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
use Watson\Validating\ValidatingTrait;
use App\Presenters\MaintenancesPresenter;
/**
* Model for Asset Maintenances.
@@ -27,7 +29,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
use Loggable, Presentable;
protected $presenter = MaintenancesPresenter::class;
protected $table = 'maintenances';
protected $rules = [
'asset_id' => 'required|integer',
@@ -118,6 +120,12 @@ class Maintenance extends SnipeModel implements ICompanyableChild
];
}
public function isDeletable()
{
return Gate::allows('delete', $this);
}
public function setIsWarrantyAttribute($value)
{
if ($value == '') {
@@ -315,4 +323,18 @@ class Maintenance extends SnipeModel implements ICompanyableChild
{
return $query->leftJoin('users as admin_sort', 'maintenances.created_by', '=', 'admin_sort.id')->select('maintenances.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
}
public function scopeOrderByAssetModelName($query, $order)
{
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
->leftjoin('models as maintained_asset_model', 'maintained_asset_model.id', '=', 'maintained_asset.model_id')
->orderBy('maintained_asset_model.name', $order);
}
public function scopeOrderByAssetModelNumber($query, $order)
{
return $query->join('assets as maintained_asset', 'maintenances.asset_id', '=', 'maintained_asset.id')
->leftjoin('models as maintained_asset_model', 'maintained_asset_model.id', '=', 'maintained_asset.model_id')
->orderBy('maintained_asset_model.model_number', $order);
}
}
+1 -1
View File
@@ -116,7 +116,7 @@ class Manufacturer extends SnipeModel
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
+14 -2
View File
@@ -2,6 +2,7 @@
namespace App\Models;
use App\Http\Traits\UniqueUndeletedTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -13,36 +14,47 @@ class ReportTemplate extends Model
{
use HasFactory;
use SoftDeletes;
use UniqueUndeletedTrait;
use ValidatingTrait;
protected $table = 'report_templates';
protected $casts = [
'options' => 'array',
'is_shared' => 'boolean',
];
protected $fillable = [
'created_by',
'name',
'options',
'is_shared',
];
protected $rules = [
'name' => [
'required',
'string',
'unique_undeleted:report_templates,name',
],
'options' => [
'required',
'array',
],
'is_shared' => [
'boolean',
],
];
protected static function booted()
{
// Scope to current user
// Scope to current user or if template is shared
static::addGlobalScope(
'current_user', function (Builder $builder) {
if (auth()->check()) {
$builder->where('created_by', auth()->id());
$builder->where('created_by', auth()->id())
->orWhere('is_shared', 1);
}
}
);
+1 -1
View File
@@ -79,7 +79,7 @@ class Statuslabel extends SnipeModel
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -158,7 +158,7 @@ class Supplier extends SnipeModel
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->withTrashed();
}
/**
+1 -1
View File
@@ -20,7 +20,7 @@ trait CompanyableTrait
if (__CLASS__ != 'App\Models\Location') {
static::addGlobalScope(new CompanyableScope);
} else {
if (Setting::getSettings()->scope_locations_fmcs == 1) {
if (Setting::getSettings()?->scope_locations_fmcs == 1) {
static::addGlobalScope(new CompanyableScope);
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\Observers;
use App\Models\Actionlog;
use App\Models\Location;
class LocationObserver
{
/**
* Listen to the User created event.
*
* @param Location $location
* @return void
*/
public function updating(Location $location)
{
$changed = [];
foreach ($location->getRawOriginal() as $key => $value) {
// Check and see if the value changed
if ($location->getRawOriginal()[$key] != $location->getAttributes()[$key]) {
$changed[$key]['old'] = $location->getRawOriginal()[$key];
$changed[$key]['new'] = $location->getAttributes()[$key];
}
}
if (count($changed) > 0) {
$logAction = new Actionlog();
$logAction->item_type = Location::class;
$logAction->item_id = $location->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->created_by = auth()->id();
$logAction->log_meta = json_encode($changed);
$logAction->logaction('update');
}
}
/**
* Listen to the Location created event when
* a new location is created.
*
* @param Location $location
* @return void
*/
public function created(Location $location)
{
$logAction = new Actionlog();
$logAction->item_type = Location::class;
$logAction->item_id = $location->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->created_by = auth()->id();
if($location->imported) {
$logAction->setActionSource('importer');
}
$logAction->logaction('create');
}
/**
* Listen to the Location deleting event.
*
* @param Location $location
* @return void
*/
public function deleting(Location $location)
{
$logAction = new Actionlog();
$logAction->item_type = Location::class;
$logAction->item_id = $location->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->created_by = auth()->id();
$logAction->logaction('delete');
}
public function restoring(Location $location)
{
$logAction = new Actionlog();
$logAction->item_type = Location::class;
$logAction->item_id = $location->id;
$logAction->created_at = date('Y-m-d H:i:s');
$logAction->created_by = auth()->id();
$logAction->logaction('restore');
}
}
+2
View File
@@ -181,6 +181,7 @@ class AccessoryPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'accessoriesActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
@@ -250,6 +251,7 @@ class AccessoryPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'accessoriesInOutFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+1
View File
@@ -268,6 +268,7 @@ class AssetAuditPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'hardwareAuditFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
return json_encode($layout);
+5 -3
View File
@@ -17,6 +17,7 @@ class AssetModelPresenter extends Presenter
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print',
],
[
'field' => 'id',
@@ -214,6 +215,7 @@ class AssetModelPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'modelsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
return json_encode($layout);
@@ -304,10 +306,10 @@ class AssetModelPresenter extends Presenter
public function formattedNameLink() {
if (auth()->user()->can('models.view', $this)) {
return '<a href="'.route('models.show', e($this->id)).'">'.e($this->name).'</a>';
if (auth()->user()->can('view', ['\App\Models\AssetModel', $this])) {
return '<a href="'.route('models.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
}
return $this->name;
return '<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
}
}
+3
View File
@@ -23,6 +23,7 @@ class AssetPresenter extends Presenter
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print'
], [
'field' => 'id',
'searchable' => false,
@@ -358,6 +359,7 @@ class AssetPresenter extends Presenter
'visible' => true,
'formatter' => 'hardwareInOutFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
$layout[] = [
@@ -368,6 +370,7 @@ class AssetPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'hardwareActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
return json_encode($layout);
+12 -3
View File
@@ -18,6 +18,8 @@ class CategoryPresenter extends Presenter
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print',
],
[
'field' => 'id',
@@ -127,6 +129,7 @@ class CategoryPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'categoriesActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
@@ -157,10 +160,16 @@ class CategoryPresenter extends Presenter
public function formattedNameLink() {
if (auth()->user()->can('view', ['\App\Models\Category', $this])) {
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('categories.show', e($this->id)).'">'.e($this->name).'</a>';
// We use soft-deletes for categories, but we don't give you a way to restore them right now. This would be the method we'd use when that happens
// if (auth()->user()->can('view', ['\App\Models\Category', $this])) {
// return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('models.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
// }
if ((auth()->user()->can('view', ['\App\Models\Category', $this])) && ($this->deleted_at=='')) {
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('categories.show', e($this->id)).'">'.e($this->name).'</a>';
}
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').e($this->name);
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
}
}
+1
View File
@@ -152,6 +152,7 @@ class CompanyPresenter extends Presenter
'visible' => true,
'formatter' => 'companiesActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+2
View File
@@ -186,6 +186,7 @@ class ComponentPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'componentsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
return json_encode($layout);
@@ -241,6 +242,7 @@ class ComponentPresenter extends Presenter
'visible' => true,
'formatter' => 'componentsInOutFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+53
View File
@@ -181,12 +181,65 @@ class ConsumablePresenter extends Presenter
'visible' => true,
'formatter' => 'consumablesActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
return json_encode($layout);
}
public static function checkedOut()
{
$layout = [
[
'field' => 'avatar',
'searchable' => false,
'sortable' => false,
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'imageFormatter',
],
[
'field' => 'user',
'searchable' => false,
'sortable' => false,
'title' => trans('general.name'),
'visible' => true,
'formatter' => 'usersLinkObjFormatter',
],
[
'field' => 'created_at',
'searchable' => false,
'sortable' => false,
'title' => trans('general.date'),
'visible' => true,
'formatter' => 'dateDisplayFormatter',
],
[
'field' => 'note',
'searchable' => false,
'sortable' => false,
'title' => trans('general.notes'),
'visible' => true,
],
[
'field' => 'created_by',
'searchable' => false,
'sortable' => false,
'title' => trans('general.created_by'),
'visible' => true,
'formatter' => 'usersLinkObjFormatter',
],
];
return json_encode($layout);
}
/**
* Url to view this item.
* @return string
+1
View File
@@ -111,6 +111,7 @@ class DepartmentPresenter extends Presenter
'visible' => true,
'formatter' => 'departmentsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+1
View File
@@ -97,6 +97,7 @@ class DepreciationPresenter extends Presenter
'visible' => true,
'formatter' => 'depreciationsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+1
View File
@@ -69,6 +69,7 @@ class GroupPresenter extends Presenter
'visible' => true,
'formatter' => 'groupsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+3
View File
@@ -213,6 +213,7 @@ class LicensePresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'licensesActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
];
return json_encode($layout);
@@ -307,6 +308,8 @@ class LicensePresenter extends Presenter
'title' => trans('general.checkin').'/'.trans('general.checkout'),
'visible' => true,
'formatter' => 'licenseSeatInOutFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+4
View File
@@ -20,6 +20,7 @@ class LocationPresenter extends Presenter
'formatter' => 'checkboxEnabledFormatter',
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print',
], [
'field' => 'id',
'searchable' => false,
@@ -266,6 +267,8 @@ class LocationPresenter extends Presenter
'title' => trans('table.actions'),
'visible' => true,
'formatter' => 'locationsActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
@@ -334,6 +337,7 @@ class LocationPresenter extends Presenter
'title' => trans('table.actions'),
'formatter' => 'accessoriesInOutFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+8
View File
@@ -81,6 +81,14 @@ class MaintenancesPresenter extends Presenter
'visible' => false,
'formatter' => 'modelsLinkObjFormatter',
], [
'field' => 'model.model_number',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.model_no'),
'visible' => true,
],
[
'field' => 'supplier',
'searchable' => true,
'sortable' => true,
+3
View File
@@ -18,6 +18,8 @@ class ManufacturerPresenter extends Presenter
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print',
],
[
'field' => 'id',
@@ -168,6 +170,7 @@ class ManufacturerPresenter extends Presenter
'visible' => true,
'formatter' => 'manufacturersActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+2
View File
@@ -17,6 +17,8 @@ class SupplierPresenter extends Presenter
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print'
],
[
'field' => 'id',
@@ -59,6 +59,8 @@ class UploadedFilesPresenter extends Presenter
'title' => trans('general.download'),
'visible' => true,
'formatter' => 'fileDownloadButtonsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
[
'field' => 'note',
@@ -93,6 +95,7 @@ class UploadedFilesPresenter extends Presenter
'visible' => true,
'formatter' => 'deleteUploadFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
+4 -2
View File
@@ -26,6 +26,7 @@ class UserPresenter extends Presenter
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
'printIgnore' => true,
'class' => 'hidden-print',
],
[
'field' => 'id',
@@ -432,6 +433,7 @@ class UserPresenter extends Presenter
'visible' => true,
'formatter' => 'usersActionsFormatter',
'printIgnore' => true,
'class' => 'hidden-print',
],
];
@@ -551,9 +553,9 @@ class UserPresenter extends Presenter
public function formattedNameLink() {
if (auth()->user()->can('view', ['\App\Models\User', $this])) {
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').'<a href="'.route('users.show', e($this->id)).'">'.e($this->display_name).'</a>';
return '<a href="'.route('users.show', e($this->id)).'" class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</a>';
}
return ($this->tag_color ? "<i class='fa-solid fa-fw fa-square' style='color: ".e($this->tag_color)."' aria-hidden='true'></i>" : '').e($this->display_name);
return '<span class="'. (($this->deleted_at!='') ? 'deleted' : '').'">'.e($this->display_name).'</span>';
}
}
+11 -8
View File
@@ -7,23 +7,25 @@ use App\Models\Asset;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Location;
use App\Models\Maintenance;
use App\Models\User;
use App\Models\Setting;
use App\Models\SnipeSCIMConfig;
use App\Models\User;
use App\Observers\AccessoryObserver;
use App\Observers\AssetObserver;
use App\Observers\UserObserver;
use App\Observers\ComponentObserver;
use App\Observers\ConsumableObserver;
use App\Observers\LicenseObserver;
use App\Observers\SettingObserver;
use App\Observers\LocationObserver;
use App\Observers\MaintenanceObserver;
use App\Observers\SettingObserver;
use App\Observers\UserObserver;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
/**
* This service provider handles setting the observers on models
@@ -68,14 +70,15 @@ class AppServiceProvider extends ServiceProvider
\Illuminate\Pagination\Paginator::useBootstrap();
Schema::defaultStringLength(191);
Asset::observe(AssetObserver::class);
Maintenance::observe(MaintenanceObserver::class);
User::observe(UserObserver::class);
Accessory::observe(AccessoryObserver::class);
Asset::observe(AssetObserver::class);
Component::observe(ComponentObserver::class);
Consumable::observe(ConsumableObserver::class);
License::observe(LicenseObserver::class);
Location::observe(LocationObserver::class);
Maintenance::observe(MaintenanceObserver::class);
Setting::observe(SettingObserver::class);
User::observe(UserObserver::class);
}
/**
+4 -2
View File
@@ -268,9 +268,10 @@ class BreadcrumbsServiceProvider extends ServiceProvider
Breadcrumbs::for('fieldsets.create', fn (Trail $trail) =>
$trail->parent('fields.index', route('fields.index'))
->push(trans('general.create'), route('fieldsets.create'))
->push(trans('admin/custom_fields/general.create_fieldset'), route('fieldsets.create'))
);
Breadcrumbs::for('fieldsets.show', fn (Trail $trail, CustomFieldset $fieldset) =>
$trail->parent('fields.index', route('fields.index'))
->push($fieldset->name, route('fields.index'))
@@ -440,7 +441,8 @@ class BreadcrumbsServiceProvider extends ServiceProvider
Breadcrumbs::for('maintenances.edit', fn (Trail $trail, Maintenance $maintenance) =>
$trail->parent('maintenances.index', route('maintenances.index'))
->push(trans('general.breadcrumb_button_actions.edit_item', ['name' => $maintenance->name]), route('maintenances.edit', $maintenance))
->push($maintenance->name, route('maintenances.show', $maintenance))
->push(trans('general.update', ['name' => $maintenance->name]), route('maintenances.edit', $maintenance))
);
+2
View File
@@ -3,6 +3,7 @@
namespace App\Providers;
use App\Listeners\CheckoutableListener;
use App\Listeners\CheckoutablesCheckedOutInBulkListener;
use App\Listeners\LogListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@@ -31,5 +32,6 @@ class EventServiceProvider extends ServiceProvider
protected $subscribe = [
LogListener::class,
CheckoutableListener::class,
CheckoutablesCheckedOutInBulkListener::class,
];
}
@@ -85,6 +85,10 @@ class SettingsServiceProvider extends ServiceProvider
return 'models/';
});
app()->singleton('assets_upload_url', function () {
return 'assets/';
});
app()->singleton('licenses_upload_url', function () {
return 'licenses/';
});
@@ -179,6 +183,10 @@ class SettingsServiceProvider extends ServiceProvider
return 'components/';
});
app()->singleton('maintenances_upload_url', function () {
return 'maintenances/';
});
// Set the monetary locale to the configured locale to make helper::parseFloat work.
setlocale(LC_MONETARY, config('app.locale'));
setlocale(LC_NUMERIC, config('app.locale'));
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Traits;
trait DisablesDebugbar
{
public function disableDebugbar()
{
if (class_exists(\Fruitcake\LaravelDebugbar\Facades\Debugbar::class)) {
\Fruitcake\LaravelDebugbar\Facades\Debugbar::disable();
}
}
}
+4 -3
View File
@@ -31,7 +31,6 @@
"alek13/slack": "^2.0",
"arietimmerman/laravel-scim-server": "dev-master",
"bacon/bacon-qr-code": "^2.0",
"barryvdh/laravel-debugbar": "^3.13",
"doctrine/cache": "^1.10",
"doctrine/dbal": "^3.1",
"doctrine/instantiator": "^1.3",
@@ -55,7 +54,7 @@
"laravelcollective/html": "6.x-dev",
"league/csv": "^9.7",
"league/flysystem-aws-s3-v3": "^3.0",
"livewire/livewire": "^3.5",
"livewire/livewire": "^4.0",
"neitanod/forceutf8": "^2.0",
"nesbot/carbon": "^3.0",
"nunomaduro/collision": "^8.1",
@@ -84,6 +83,7 @@
"ext-exif": "*"
},
"require-dev": {
"fruitcake/laravel-debugbar": "^4.0",
"larastan/larastan": "^2.9",
"laravel/telescope": "^5.11",
"mockery/mockery": "^1.4",
@@ -125,7 +125,8 @@
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi",
"@php artisan vendor:publish --force --tag=livewire:assets --ansi"
"@php artisan vendor:publish --force --tag=livewire:assets --ansi",
"@php fix-permissions.php"
],
"post-create-project-cmd": [
"php artisan key:generate"
Generated
+321 -209
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b1dfc90a20cecf851224ea8a5c71f26d",
"content-hash": "58603818d33285d78c0c8655385e7c0e",
"packages": [
{
"name": "alek13/slack",
@@ -340,91 +340,6 @@
},
"time": "2022-12-07T17:46:57+00:00"
},
{
"name": "barryvdh/laravel-debugbar",
"version": "v3.16.0",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "f265cf5e38577d42311f1a90d619bcd3740bea23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/f265cf5e38577d42311f1a90d619bcd3740bea23",
"reference": "f265cf5e38577d42311f1a90d619bcd3740bea23",
"shasum": ""
},
"require": {
"illuminate/routing": "^9|^10|^11|^12",
"illuminate/session": "^9|^10|^11|^12",
"illuminate/support": "^9|^10|^11|^12",
"php": "^8.1",
"php-debugbar/php-debugbar": "~2.2.0",
"symfony/finder": "^6|^7"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"orchestra/testbench-dusk": "^7|^8|^9|^10",
"phpunit/phpunit": "^9.5.10|^10|^11",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar"
},
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.16-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Barryvdh\\Debugbar\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "PHP Debugbar integration for Laravel",
"keywords": [
"debug",
"debugbar",
"dev",
"laravel",
"profiler",
"webprofiler"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.0"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2025-07-14T11:56:43+00:00"
},
{
"name": "brick/math",
"version": "0.14.0",
@@ -4458,16 +4373,16 @@
},
{
"name": "livewire/livewire",
"version": "v3.6.4",
"version": "v4.1.4",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
"reference": "ef04be759da41b14d2d129e670533180a44987dc"
"reference": "4697085e02a1f5f11410a1b5962400e3539f8843"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/ef04be759da41b14d2d129e670533180a44987dc",
"reference": "ef04be759da41b14d2d129e670533180a44987dc",
"url": "https://api.github.com/repos/livewire/livewire/zipball/4697085e02a1f5f11410a1b5962400e3539f8843",
"reference": "4697085e02a1f5f11410a1b5962400e3539f8843",
"shasum": ""
},
"require": {
@@ -4522,7 +4437,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v3.6.4"
"source": "https://github.com/livewire/livewire/tree/v4.1.4"
},
"funding": [
{
@@ -4530,7 +4445,7 @@
"type": "github"
}
],
"time": "2025-07-17T05:12:15+00:00"
"time": "2026-02-09T22:59:54+00:00"
},
{
"name": "masterminds/html5",
@@ -5840,79 +5755,6 @@
},
"time": "2025-12-30T16:16:35+00:00"
},
{
"name": "php-debugbar/php-debugbar",
"version": "v2.2.4",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/3146d04671f51f69ffec2a4207ac3bdcf13a9f35",
"reference": "3146d04671f51f69ffec2a4207ac3bdcf13a9f35",
"shasum": ""
},
"require": {
"php": "^8",
"psr/log": "^1|^2|^3",
"symfony/var-dumper": "^4|^5|^6|^7"
},
"replace": {
"maximebf/debugbar": "self.version"
},
"require-dev": {
"dbrekelmans/bdi": "^1",
"phpunit/phpunit": "^8|^9",
"symfony/panther": "^1|^2.1",
"twig/twig": "^1.38|^2.7|^3.0"
},
"suggest": {
"kriswallsmith/assetic": "The best way to manage assets",
"monolog/monolog": "Log using Monolog",
"predis/predis": "Redis storage"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"autoload": {
"psr-4": {
"DebugBar\\": "src/DebugBar/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
},
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "Debug bar in the browser for php application",
"homepage": "https://github.com/php-debugbar/php-debugbar",
"keywords": [
"debug",
"debug bar",
"debugbar",
"dev"
],
"support": {
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/php-debugbar/php-debugbar/tree/v2.2.4"
},
"time": "2025-07-22T14:01:30+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
@@ -9103,23 +8945,23 @@
},
{
"name": "symfony/finder",
"version": "v7.3.2",
"version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
"reference": "01b24a145bbeaa7141e75887ec904c34a6728a5f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
"url": "https://api.github.com/repos/symfony/finder/zipball/01b24a145bbeaa7141e75887ec904c34a6728a5f",
"reference": "01b24a145bbeaa7141e75887ec904c34a6728a5f",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"require-dev": {
"symfony/filesystem": "^6.4|^7.0"
"symfony/filesystem": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -9147,7 +8989,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v7.3.2"
"source": "https://github.com/symfony/finder/tree/v7.4.4"
},
"funding": [
{
@@ -9167,27 +9009,26 @@
"type": "tidelift"
}
],
"time": "2025-07-15T13:41:35+00:00"
"time": "2026-01-12T12:19:02+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v7.3.7",
"version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4"
"reference": "977a554a34cf8edc95ca351fbecb1bb1ad05cc94"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4",
"reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/977a554a34cf8edc95ca351fbecb1bb1ad05cc94",
"reference": "977a554a34cf8edc95ca351fbecb1bb1ad05cc94",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-mbstring": "~1.1",
"symfony/polyfill-php83": "^1.27"
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "^1.1"
},
"conflict": {
"doctrine/dbal": "<3.6",
@@ -9196,13 +9037,13 @@
"require-dev": {
"doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0",
"symfony/cache": "^6.4.12|^7.1.5",
"symfony/clock": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/mime": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0"
"symfony/cache": "^6.4.12|^7.1.5|^8.0",
"symfony/clock": "^6.4|^7.0|^8.0",
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
"symfony/expression-language": "^6.4|^7.0|^8.0",
"symfony/http-kernel": "^6.4|^7.0|^8.0",
"symfony/mime": "^6.4|^7.0|^8.0",
"symfony/rate-limiter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -9230,7 +9071,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.3.7"
"source": "https://github.com/symfony/http-foundation/tree/v7.4.4"
},
"funding": [
{
@@ -9250,7 +9091,7 @@
"type": "tidelift"
}
],
"time": "2025-11-08T16:41:12+00:00"
"time": "2026-01-09T12:14:21+00:00"
},
{
"name": "symfony/http-kernel",
@@ -9456,20 +9297,21 @@
},
{
"name": "symfony/mime",
"version": "v7.3.2",
"version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1"
"reference": "40945014c0a9471ccfe19673c54738fa19367a3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1",
"reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1",
"url": "https://api.github.com/repos/symfony/mime/zipball/40945014c0a9471ccfe19673c54738fa19367a3c",
"reference": "40945014c0a9471ccfe19673c54738fa19367a3c",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
@@ -9484,11 +9326,11 @@
"egulias/email-validator": "^2.1.10|^3.1|^4",
"league/html-to-markdown": "^5.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/property-access": "^6.4|^7.0",
"symfony/property-info": "^6.4|^7.0",
"symfony/serializer": "^6.4.3|^7.0.3"
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/property-access": "^6.4|^7.0|^8.0",
"symfony/property-info": "^6.4|^7.0|^8.0",
"symfony/serializer": "^6.4.3|^7.0.3|^8.0"
},
"type": "library",
"autoload": {
@@ -9520,7 +9362,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.3.2"
"source": "https://github.com/symfony/mime/tree/v7.4.4"
},
"funding": [
{
@@ -9540,7 +9382,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T13:41:35+00:00"
"time": "2026-01-08T16:12:55+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -10872,16 +10714,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v7.3.3",
"version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f"
"reference": "0e4769b46a0c3c62390d124635ce59f66874b282"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e4769b46a0c3c62390d124635ce59f66874b282",
"reference": "0e4769b46a0c3c62390d124635ce59f66874b282",
"shasum": ""
},
"require": {
@@ -10893,10 +10735,10 @@
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/uid": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0|^8.0",
"symfony/http-kernel": "^6.4|^7.0|^8.0",
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/uid": "^6.4|^7.0|^8.0",
"twig/twig": "^3.12"
},
"bin": [
@@ -10935,7 +10777,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v7.3.3"
"source": "https://github.com/symfony/var-dumper/tree/v7.4.4"
},
"funding": [
{
@@ -10955,7 +10797,7 @@
"type": "tidelift"
}
],
"time": "2025-08-13T11:49:31+00:00"
"time": "2026-01-01T22:13:48+00:00"
},
{
"name": "tabuna/breadcrumbs",
@@ -12771,6 +12613,108 @@
],
"time": "2025-09-10T09:51:40+00:00"
},
{
"name": "fruitcake/laravel-debugbar",
"version": "v4.0.7",
"source": {
"type": "git",
"url": "https://github.com/fruitcake/laravel-debugbar.git",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/a9cc62c81cd0bda4ca7410229487638d7df786be",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be",
"shasum": ""
},
"require": {
"illuminate/routing": "^11|^12",
"illuminate/session": "^11|^12",
"illuminate/support": "^11|^12",
"php": "^8.2",
"php-debugbar/php-debugbar": "^3.1",
"php-debugbar/symfony-bridge": "^1.1"
},
"replace": {
"barryvdh/laravel-debugbar": "self.version"
},
"require-dev": {
"larastan/larastan": "^3",
"laravel/octane": "^2",
"laravel/pennant": "^1",
"laravel/pint": "^1",
"laravel/telescope": "^5.16",
"livewire/livewire": "^3.7|^4",
"mockery/mockery": "^1.3.3",
"orchestra/testbench-dusk": "^9|^10",
"php-debugbar/twig-bridge": "^2.0",
"phpstan/phpstan-phpunit": "^2",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^11",
"shipmonk/phpstan-rules": "^4.3"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Debugbar": "Fruitcake\\LaravelDebugbar\\Facades\\Debugbar"
},
"providers": [
"Fruitcake\\LaravelDebugbar\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Fruitcake\\LaravelDebugbar\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fruitcake",
"homepage": "https://fruitcake.nl"
},
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "PHP Debugbar integration for Laravel",
"keywords": [
"barryvdh",
"debug",
"debugbar",
"dev",
"laravel",
"profiler",
"webprofiler"
],
"support": {
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.7"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2026-02-06T20:53:50+00:00"
},
{
"name": "hamcrest/hamcrest-php",
"version": "v2.1.1",
@@ -13608,6 +13552,174 @@
},
"time": "2022-02-21T01:04:05+00:00"
},
{
"name": "php-debugbar/php-debugbar",
"version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "a1f4e156811f000efb6e118178832581fab94f11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/a1f4e156811f000efb6e118178832581fab94f11",
"reference": "a1f4e156811f000efb6e118178832581fab94f11",
"shasum": ""
},
"require": {
"php": "^8.2",
"psr/log": "^1|^2|^3",
"symfony/var-dumper": "^5.4|^6|^7|^8"
},
"replace": {
"maximebf/debugbar": "self.version"
},
"require-dev": {
"dbrekelmans/bdi": "^1.4",
"friendsofphp/php-cs-fixer": "^3.92",
"monolog/monolog": "^3.9",
"php-debugbar/doctrine-bridge": "^3@dev",
"php-debugbar/monolog-bridge": "^1@dev",
"php-debugbar/symfony-bridge": "^1@dev",
"php-debugbar/twig-bridge": "^2@dev",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^10",
"predis/predis": "^3.3",
"shipmonk/phpstan-rules": "^4.3",
"symfony/browser-kit": "^6.4|7.0",
"symfony/dom-crawler": "^6.4|^7",
"symfony/event-dispatcher": "^5.4|^6.4|^7.3|^8.0",
"symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0",
"symfony/mailer": "^5.4|^6.4|^7.3|^8.0",
"symfony/panther": "^1|^2.1",
"twig/twig": "^3.11.2"
},
"suggest": {
"php-debugbar/doctrine-bridge": "To integrate Doctrine with php-debugbar.",
"php-debugbar/monolog-bridge": "To integrate Monolog with php-debugbar.",
"php-debugbar/symfony-bridge": "To integrate Symfony with php-debugbar.",
"php-debugbar/twig-bridge": "To integrate Twig with php-debugbar."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"DebugBar\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
},
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "Debug bar in the browser for php application",
"homepage": "https://github.com/php-debugbar/php-debugbar",
"keywords": [
"debug",
"debug bar",
"debugbar",
"dev",
"profiler",
"toolbar"
],
"support": {
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.2.2"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2026-01-25T20:59:56+00:00"
},
{
"name": "php-debugbar/symfony-bridge",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/symfony-bridge.git",
"reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/symfony-bridge/zipball/e37d2debe5d316408b00d0ab2688d9c2cf59b5ad",
"reference": "e37d2debe5d316408b00d0ab2688d9c2cf59b5ad",
"shasum": ""
},
"require": {
"php": "^8.2",
"php-debugbar/php-debugbar": "^3.1",
"symfony/http-foundation": "^5.4|^6.4|^7.3|^8.0"
},
"require-dev": {
"dbrekelmans/bdi": "^1.4",
"phpunit/phpunit": "^10",
"symfony/browser-kit": "^6|^7",
"symfony/dom-crawler": "^6|^7",
"symfony/mailer": "^5.4|^6.4|^7.3|^8.0",
"symfony/panther": "^1|^2.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"DebugBar\\Bridge\\Symfony\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
},
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "Symfony bridge for PHP Debugbar",
"homepage": "https://github.com/php-debugbar/php-debugbar",
"keywords": [
"debugbar",
"dev",
"symfony"
],
"support": {
"issues": "https://github.com/php-debugbar/symfony-bridge/issues",
"source": "https://github.com/php-debugbar/symfony-bridge/tree/v1.1.0"
},
"time": "2026-01-15T14:47:34+00:00"
},
{
"name": "php-mock/php-mock",
"version": "2.6.2",
+301 -102
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
return [
/*
@@ -10,98 +12,53 @@ return [
| Debugbar is enabled by default, when debug is set to true in app.php.
| You can override the value by setting enable to true or false instead of null.
|
*/
'enabled' => null,
/*
|--------------------------------------------------------------------------
| Storage settings
|--------------------------------------------------------------------------
|
| DebugBar stores data for session/ajax requests.
| You can disable this, so the debugbar stores data in headers/session,
| but this can cause problems with large data collectors.
| By default, file storage (in the storage folder) is used. Redis and PDO
| can also be used. For PDO, run the package migrations first.
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
*/
'storage' => [
'enabled' => true,
'driver' => 'file', // redis, file, pdo
'path' => storage_path().'/debugbar', // For file driver
'connection' => null, // Leave null for default connection (Redis/PDO)
'enabled' => env('DEBUGBAR_ENABLED'),
'collect_jobs' => env('DEBUGBAR_COLLECT_JOBS', false),
'except' => [
'telescope*',
'horizon*',
'_boost/browser-logs',
],
/*
|--------------------------------------------------------------------------
| Vendors
|--------------------------------------------------------------------------
|
| Vendor files are included by default, but can be set to false.
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
| and for js: jquery and and highlight.js
| So if you want syntax highlighting, set it to true.
| jQuery is set to not conflict with existing jQuery scripts.
|
*/
'include_vendors' => true,
/*
|--------------------------------------------------------------------------
| Capture Ajax Requests
|--------------------------------------------------------------------------
|
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
| you can use this option to disable sending the data through the headers.
|
*/
'capture_ajax' => true,
/*
|--------------------------------------------------------------------------
| Clockwork integration
|--------------------------------------------------------------------------
|
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
*/
'clockwork' => true,
/*
|--------------------------------------------------------------------------
| DataCollectors
|--------------------------------------------------------------------------
|
| Enable/disable DataCollectors
|
*/
|--------------------------------------------------------------------------
| DataCollectors
|--------------------------------------------------------------------------
|
| Enable/disable DataCollectors
|
*/
'collectors' => [
'phpinfo' => true, // Php version
'messages' => true, // Messages
'time' => true, // Time Datalogger
'memory' => true, // Memory usage
'exceptions' => true, // Exception displayer
'log' => true, // Logs from Monolog (merged in messages if enabled)
'db' => true, // Show database (PDO) queries and bindings
'views' => true, // Views with their data
'route' => true, // Current route information
'laravel' => true, // Laravel version and environment
'events' => true, // All events fired
'default_request' => false, // Regular or special Symfony request logger
'symfony_request' => true, // Only one can be enabled..
'mail' => true, // Catch mail messages
'logs' => true, // Add the latest log messages
'files' => true, // Show the included files
'config' => false, // Display config settings
'auth' => true, // Display Laravel authentication status
'gate' => true, // Display Laravel Gate checks
'session' => true, // Display session data
'phpinfo' => env('DEBUGBAR_COLLECTORS_PHPINFO', true), // Php version
'messages' => env('DEBUGBAR_COLLECTORS_MESSAGES', true), // Messages
'time' => env('DEBUGBAR_COLLECTORS_TIME', true), // Time Datalogger
'memory' => env('DEBUGBAR_COLLECTORS_MEMORY', true), // Memory usage
'exceptions' => env('DEBUGBAR_COLLECTORS_EXCEPTIONS', true), // Exception displayer
'log' => env('DEBUGBAR_COLLECTORS_LOG', true), // Logs from Monolog (merged in messages if enabled)
'db' => env('DEBUGBAR_COLLECTORS_DB', true), // Show database (PDO) queries and bindings
'views' => env('DEBUGBAR_COLLECTORS_VIEWS', true), // Views with their data
'route' => env('DEBUGBAR_COLLECTORS_ROUTE', true), // Current route information
'auth' => env('DEBUGBAR_COLLECTORS_AUTH', true), // Display Laravel authentication status
'gate' => env('DEBUGBAR_COLLECTORS_GATE', true), // Display Laravel Gate checks
'session' => env('DEBUGBAR_COLLECTORS_SESSION', true), // Display session data
'symfony_request' => env('DEBUGBAR_COLLECTORS_SYMFONY_REQUEST', false), // Default Request Data
'mail' => env('DEBUGBAR_COLLECTORS_MAIL', true), // Catch mail messages
'laravel' => env('DEBUGBAR_COLLECTORS_LARAVEL', true), // Laravel version and environment
'events' => env('DEBUGBAR_COLLECTORS_EVENTS', true), // All events fired
'logs' => env('DEBUGBAR_COLLECTORS_LOGS', true), // Add the latest log messages
'config' => env('DEBUGBAR_COLLECTORS_CONFIG', false), // Display config settings
'cache' => env('DEBUGBAR_COLLECTORS_CACHE', true), // Display cache events
'models' => env('DEBUGBAR_COLLECTORS_MODELS', true), // Display models
'livewire' => env('DEBUGBAR_COLLECTORS_LIVEWIRE', true), // Display Livewire (when available)
'inertia' => env('DEBUGBAR_COLLECTORS_INERTIA', true), // Display Inertia (when available)
'jobs' => env('DEBUGBAR_COLLECTORS_JOBS', true), // Display dispatched jobs
'pennant' => env('DEBUGBAR_COLLECTORS_PENNANT', true), // Display Pennant feature flags
'http_client' => env('DEBUGBAR_COLLECTORS_HTTP_CLIENT', true), // Display HTTP Client requests
],
/*
@@ -114,56 +71,298 @@ return [
*/
'options' => [
'time' => [
'memory_usage' => env('DEBUGBAR_OPTIONS_TIME_MEMORY_USAGE', false), // Calculated by subtracting memory start and end, it may be inaccurate
],
'messages' => [
'trace' => env('DEBUGBAR_OPTIONS_MESSAGES_TRACE', true), // Trace the origin of the debug message
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
'capture_dumps' => env('DEBUGBAR_OPTIONS_MESSAGES_CAPTURE_DUMPS', false), // Capture laravel `dump();` as message
'timeline' => env('DEBUGBAR_OPTIONS_MESSAGES_TIMELINE', true), // Add messages to the timeline
],
'memory' => [
'reset_peak' => env('DEBUGBAR_OPTIONS_MEMORY_RESET_PEAK', false), // run memory_reset_peak_usage before collecting
'with_baseline' => env('DEBUGBAR_OPTIONS_MEMORY_WITH_BASELINE', false), // Set boot memory usage as memory peak baseline
'precision' => (int) env('DEBUGBAR_OPTIONS_MEMORY_PRECISION', 0), // Memory rounding precision
],
'auth' => [
'show_name' => false, // Also show the users name/email in the debugbar
'show_name' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_NAME', false), // Also show the users name/email in the debugbar
'show_guards' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_GUARDS', true), // Show the guards that are used
],
'gate' => [
'trace' => false, // Trace the origin of the Gate checks
'timeline' => env('DEBUGBAR_OPTIONS_GATE_TIMELINE', false), // Add mails to the timeline
],
'db' => [
'with_params' => true, // Render SQL with the parameters substituted
'timeline' => true, // Add the queries to the timeline
'backtrace' => true, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files.
'explain' => [ // EXPERIMENTAL: Show EXPLAIN output on queries
'enabled' => false,
'types' => ['SELECT'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+
'with_params' => env('DEBUGBAR_OPTIONS_WITH_PARAMS', true), // Render SQL with the parameters substituted
'exclude_paths' => [ // Paths to exclude entirely from the collector
//'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries
],
'hints' => true, // Show hints for common mistakes
'backtrace' => env('DEBUGBAR_OPTIONS_DB_BACKTRACE', true), // Use a backtrace to find the origin of the query in your files.
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
'timeline' => env('DEBUGBAR_OPTIONS_DB_TIMELINE', true), // Add the queries to the timeline
'duration_background' => env('DEBUGBAR_OPTIONS_DB_DURATION_BACKGROUND', true), // Show shaded background on each query relative to how long it took to execute.
'explain' => [ // Show EXPLAIN output on queries
'enabled' => env('DEBUGBAR_OPTIONS_DB_EXPLAIN_ENABLED', false),
],
'only_slow_queries' => env('DEBUGBAR_OPTIONS_DB_ONLY_SLOW_QUERIES', true), // Only track queries that last longer than `slow_threshold`
'slow_threshold' => env('DEBUGBAR_OPTIONS_DB_SLOW_THRESHOLD', false), // Max query execution time (ms). Exceeding queries will be highlighted
'memory_usage' => env('DEBUGBAR_OPTIONS_DB_MEMORY_USAGE', false), // Show queries memory usage
'soft_limit' => (int) env('DEBUGBAR_OPTIONS_DB_SOFT_LIMIT', 100), // After the soft limit, no parameters/backtrace are captured
'hard_limit' => (int) env('DEBUGBAR_OPTIONS_DB_HARD_LIMIT', 500), // After the hard limit, queries are ignored
],
'mail' => [
'full_log' => false,
'timeline' => env('DEBUGBAR_OPTIONS_MAIL_TIMELINE', true), // Add mails to the timeline
'show_body' => env('DEBUGBAR_OPTIONS_MAIL_SHOW_BODY', true),
],
'views' => [
'data' => false, //Note: Can slow down the application, because the data can be quite large..
'timeline' => env('DEBUGBAR_OPTIONS_VIEWS_TIMELINE', true), // Add the views to the timeline
'data' => env('DEBUGBAR_OPTIONS_VIEWS_DATA', false), // True for all data, 'keys' for only names, false for no parameters.
'group' => (int) env('DEBUGBAR_OPTIONS_VIEWS_GROUP', 50), // Group duplicate views. Pass value to auto-group, or true/false to force
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
'vendor/filament', // Exclude Filament components by default
],
],
'inertia' => [
'pages' => env('DEBUGBAR_OPTIONS_VIEWS_INERTIA_PAGES', 'js/Pages'), // Path for Inertia views
],
'route' => [
'label' => true, // show complete route on bar
'label' => env('DEBUGBAR_OPTIONS_ROUTE_LABEL', true), // Show complete route on bar
],
'session' => [
'masked' => [], // List of keys that are masked
],
'symfony_request' => [
'label' => env('DEBUGBAR_OPTIONS_SYMFONY_REQUEST_LABEL', true), // Show route on bar
'masked' => [], // List of keys that are masked
],
'events' => [
'data' => env('DEBUGBAR_OPTIONS_EVENTS_DATA', false), // Collect events data
'listeners' => env('DEBUGBAR_OPTIONS_EVENTS_LISTENERS', false), // Add listeners to the events data
'excluded' => [], // Example: ['eloquent.*', 'composing', Illuminate\Cache\Events\CacheHit::class]
],
'logs' => [
'file' => null,
'file' => env('DEBUGBAR_OPTIONS_LOGS_FILE'),
],
'cache' => [
'values' => env('DEBUGBAR_OPTIONS_CACHE_VALUES', true), // Collect cache values
'timeline' => env('DEBUGBAR_OPTIONS_CACHE_TIMELINE', false), // Add mails to the timeline
],
'http_client' => [
'masked' => [],
'timeline' => env('DEBUGBAR_OPTIONS_HTTP_CLIENT_TIMELINE', true), // Add requests to the timeline
],
],
/**
* Add any additional DataCollectors by adding the class name of a DataCollector or invokable class.
*/
'custom_collectors' => [
// MyCollector::class => env('DEBUGBAR_COLLECTORS_MYCOLLECTOR', true),
],
/*
|--------------------------------------------------------------------------
| Editor
|--------------------------------------------------------------------------
|
| Choose your preferred editor to use when clicking file name.
|
| Supported: "sublime", "textmate", "emacs", "macvim", "codelite",
| "phpstorm", "phpstorm-remote", "idea", "idea-remote",
| "vscode", "vscode-insiders", "vscode-remote", "vscode-insiders-remote",
| "vscodium", "nova", "xdebug", "atom", "espresso",
| "netbeans", "cursor", "windsurf", "zed", "antigravity"
|
*/
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
/*
|--------------------------------------------------------------------------
| Capture Ajax Requests
|--------------------------------------------------------------------------
|
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
| you can use this option to disable sending the data through the headers.
|
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
| Note for your request to be identified as ajax requests they must either send the header
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
|
| You can defer loading the dataset, so it will be loaded with ajax after the request is done. (Experimental)
*/
'capture_ajax' => env('DEBUGBAR_CAPTURE_AJAX', true),
'add_ajax_timing' => env('DEBUGBAR_ADD_AJAX_TIMING', false),
'ajax_handler_auto_show' => env('DEBUGBAR_AJAX_HANDLER_AUTO_SHOW', true),
'ajax_handler_enable_tab' => env('DEBUGBAR_AJAX_HANDLER_ENABLE_TAB', true),
'defer_datasets' => env('DEBUGBAR_DEFER_DATASETS', false),
/*
|--------------------------------------------------------------------------
| Remote Path Mapping
|--------------------------------------------------------------------------
|
| If you are using a remote dev server, like Laravel Homestead, Docker, or
| even a remote VPS, it will be necessary to specify your path mapping.
|
| Leaving one, or both of these, empty or null will not trigger the remote
| URL changes and Debugbar will treat your editor links as local files.
|
| "remote_sites_path" is an absolute base path for your sites or projects
| in Homestead, Vagrant, Docker, or another remote development server.
|
| Example value: "/home/vagrant/Code"
|
| "local_sites_path" is an absolute base path for your sites or projects
| on your local computer where your IDE or code editor is running on.
|
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
*/
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
/*
|--------------------------------------------------------------------------
| Storage settings
|--------------------------------------------------------------------------
|
| Debugbar stores data for session/ajax requests.
| You can disable this, so the debugbar stores data in headers/session,
| but this can cause problems with large data collectors.
| By default, file storage (in the storage folder) is used. Sqlite will
| create a database file in the storage folder.
| Redis and PDO can also be used. For PDO, run the package migrations first.
|
| Warning: Enabling storage.open will allow everyone to access previous
| request, do not enable open storage in publicly available environments!
| Specify a callback if you want to limit based on IP or authentication.
| Leaving it to null will allow localhost only.
*/
'storage' => [
'enabled' => env('DEBUGBAR_STORAGE_ENABLED', true),
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
'driver' => env('DEBUGBAR_STORAGE_DRIVER', 'file'), // redis, file, sqlite, pdo, custom
'path' => env('DEBUGBAR_STORAGE_PATH', storage_path('debugbar')), // For file driver
'connection' => env('DEBUGBAR_STORAGE_CONNECTION'), // Leave null for default connection (Redis/PDO)
'provider' => env('DEBUGBAR_STORAGE_PROVIDER', ''), // Instance of StorageInterface for custom driver
],
/*
|--------------------------------------------------------------------------
| Assets
|--------------------------------------------------------------------------
|
| Vendor files are included by default, but can be set to false.
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
| Vendor files are for css: (none)
| and for js: highlight.js
| So if you want syntax highlighting, set it to true.
|
*/
'use_dist_files' => env('DEBUGBAR_USE_DIST_FILES', true),
'include_vendors' => env('DEBUGBAR_INCLUDE_VENDORS', true),
/*
|--------------------------------------------------------------------------
| Custom Error Handler for Deprecated warnings
|--------------------------------------------------------------------------
|
| When enabled, the Debugbar shows deprecated warnings for Symfony components
| in the Messages tab.
|
| You can set a custom error reporting level to filter which errors are
| handled. For example, to exclude deprecation warnings:
| E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
|
| To exclude notices, strict warnings, and deprecations:
| E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED & ~E_USER_DEPRECATED
|
| Defaults to E_ALL (all errors).
|
*/
'error_handler' => env('DEBUGBAR_ERROR_HANDLER', false),
'error_level' => env('DEBUGBAR_ERROR_LEVEL', E_ALL),
/*
|--------------------------------------------------------------------------
| Clockwork integration
|--------------------------------------------------------------------------
|
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
*/
'clockwork' => env('DEBUGBAR_CLOCKWORK', true),
/*
|--------------------------------------------------------------------------
| Inject Debugbar in Response
|--------------------------------------------------------------------------
|
| Usually, the debugbar is added just before <body>, by listening to the
| Usually, the debugbar is added just before </body>, by listening to the
| Response after the App is done. If you disable this, you have to add them
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
*/
'inject' => true,
'inject' => env('DEBUGBAR_INJECT', true),
/*
|--------------------------------------------------------------------------
| DebugBar route prefix
| Debugbar route prefix
|--------------------------------------------------------------------------
|
| Sometimes you want to set route prefix to be used by DebugBar to load
| Sometimes you want to set route prefix to be used by Debugbar to load
| its resources from. Usually the need comes from misconfigured web server or
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
*/
'route_prefix' => '_debugbar',
'route_prefix' => env('DEBUGBAR_ROUTE_PREFIX', '_debugbar'),
/*
|--------------------------------------------------------------------------
| Debugbar route middleware
|--------------------------------------------------------------------------
|
| Additional middleware to run on the Debugbar routes
*/
'route_middleware' => [],
/*
|--------------------------------------------------------------------------
| Debugbar route domain
|--------------------------------------------------------------------------
|
| By default Debugbar route served from the same domain that request served.
| To override default domain, specify it as a non-empty value.
*/
'route_domain' => env('DEBUGBAR_ROUTE_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Debugbar theme
|--------------------------------------------------------------------------
|
| Switches between light and dark theme. If set to auto it will respect system preferences
| Possible values: auto, light, dark
*/
'theme' => env('DEBUGBAR_THEME', 'auto'),
/*
|--------------------------------------------------------------------------
| Backtrace stack limit
|--------------------------------------------------------------------------
|
| By default, the Debugbar limits the number of frames returned by the 'debug_backtrace()' function.
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
*/
'debug_backtrace_limit' => (int) env('DEBUGBAR_DEBUG_BACKTRACE_LIMIT', 50),
];
+137 -33
View File
@@ -2,6 +2,81 @@
return [
/*
|---------------------------------------------------------------------------
| Component Locations
|---------------------------------------------------------------------------
|
| This value sets the root directories that'll be used to resolve view-based
| components like single and multi-file components. The make command will
| use the first directory in this array to add new component files to.
|
*/
'component_locations' => [
resource_path('views/livewire'),
],
/*
|---------------------------------------------------------------------------
| Component Namespaces
|---------------------------------------------------------------------------
|
| This value sets default namespaces that will be used to resolve view-based
| components like single-file and multi-file components. These folders'll
| also be referenced when creating new components via the make command.
|
*/
'component_namespaces' => [
'layouts' => resource_path('views/layouts'),
'pages' => resource_path('views/pages'),
],
/*
|---------------------------------------------------------------------------
| Page Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component as
| an entire page via `Route::livewire('/post/create', 'pages::create-post')`.
| In this case, the content of pages::create-post will render into $slot.
|
*/
'component_layout' => 'layouts::app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'component_placeholder' => null, // Example: 'placeholders::skeleton'
/*
|---------------------------------------------------------------------------
| Make Command
|---------------------------------------------------------------------------
| This value determines the default configuration for the artisan make command
| You can configure the component type (sfc, mfc, class) and whether to use
| the high-voltage () emoji as a prefix in the sfc|mfc component names.
|
*/
'make_command' => [
'type' => 'class', // Options: 'sfc', 'mfc', 'class'
'emoji' => false, // Options: true, false
'with' => [
'js' => false,
'css' => false,
'test' => false,
],
],
/*
|---------------------------------------------------------------------------
| Class Namespace
@@ -15,6 +90,19 @@ return [
'class_namespace' => 'App\\Livewire',
/*
|---------------------------------------------------------------------------
| Class Path
|---------------------------------------------------------------------------
|
| This value is used to specify the path where Livewire component class files
| are created when running creation commands like `artisan make:livewire`.
| This path is customizable to match your projects directory structure.
|
*/
'class_path' => app_path('Livewire'),
/*
|---------------------------------------------------------------------------
| View Path
@@ -28,30 +116,6 @@ return [
'view_path' => resource_path('views/livewire'),
/*
|---------------------------------------------------------------------------
| Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
| In this case, the view returned by CreatePost will render into $slot.
|
*/
'layout' => 'components.layouts.app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'lazy_placeholder' => null,
/*
|---------------------------------------------------------------------------
| Temporary File Uploads
@@ -145,6 +209,19 @@ return [
'inject_morph_markers' => true,
/*
|---------------------------------------------------------------------------
| Smart Wire Keys
|---------------------------------------------------------------------------
|
| Livewire uses loops and keys used within loops to generate smart keys that
| are applied to nested components that don't have them. This makes using
| nested components more reliable by ensuring that they all have keys.
|
*/
'smart_wire_keys' => true,
/*
|---------------------------------------------------------------------------
| Pagination Theme
@@ -160,18 +237,45 @@ return [
/*
|---------------------------------------------------------------------------
| URL Prefix
| Release Token
|---------------------------------------------------------------------------
|
| By default, Livewire sends network requests to {app.com}/livewire/update
| while javascript assets are served via {app.com}/livewire/livewire.js
| If you need to adjust the prefix of those urls you can do it below.
|
| Defining a prefix will result in the following url
| {app.com}/{prefix}/livewire/{update|livewire.js}
| Note: do not include the leading or trailing /
| This token is stored client-side and sent along with each request to check
| a users session to see if a new release has invalidated it. If there is
| a mismatch it will throw an error and prompt for a browser refresh.
|
*/
'url_prefix' => env('LIVEWIRE_URL_PREFIX', null),
'release_token' => 'a',
/*
|---------------------------------------------------------------------------
| CSP Safe
|---------------------------------------------------------------------------
|
| This config is used to determine if Livewire will use the CSP-safe version
| of Alpine in its bundle. This is useful for applications that are using
| strict Content Security Policy (CSP) to protect against XSS attacks.
|
*/
'csp_safe' => false,
/*
|---------------------------------------------------------------------------
| Payload Guards
|---------------------------------------------------------------------------
|
| These settings protect against malicious or oversized payloads that could
| cause denial of service. The default values should feel reasonable for
| most web applications. Each can be set to null to disable the limit.
|
*/
'payload' => [
'max_size' => 1024 * 1024, // 1MB - maximum request payload size in bytes
'max_nesting_depth' => 10, // Maximum depth of dot-notation property paths
'max_calls' => 50, // Maximum method calls per request
'max_components' => 20, // Maximum components per batch request
],
];
+6 -6
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v8.3.7',
'full_app_version' => 'v8.3.7 - build 21025-ga8c268760',
'build_version' => '21025',
'app_version' => 'v8.4.0',
'full_app_version' => 'v8.4.0 - build 21280-g91a95dbc6',
'build_version' => '21280',
'prerelease_version' => '',
'hash_version' => 'ga8c268760',
'full_hash' => 'v8.3.7-220-ga8c268760',
'branch' => 'master',
'hash_version' => 'g91a95dbc6',
'full_hash' => 'v8.4.0-475-g91a95dbc6',
'branch' => 'develop',
);
+1
View File
@@ -1 +1,2 @@
*.sqlite
*.journal
+30
View File
@@ -215,4 +215,34 @@ class CategoryFactory extends Factory
'require_acceptance' => false,
]);
}
public function sendsCheckinEmail()
{
return $this->state([
'checkin_email' => true,
]);
}
public function doesNotSendCheckinEmail()
{
return $this->state([
'checkin_email' => false,
]);
}
public function hasLocalEula()
{
return $this->state([
'use_default_eula' => false,
'eula_text' => 'Some EULA text here',
]);
}
public function withNoLocalOrGlobalEula()
{
return $this->state([
'use_default_eula' => false,
'eula_text' => '',
]);
}
}
@@ -43,6 +43,13 @@ class LicenseSeatFactory extends Factory
});
}
public function unreassignable()
{
return $this->afterMaking(function (LicenseSeat $seat) {
$seat->license->update(['reassignable' => false]);
});
}
public function notReassignable()
{
return $this->afterMaking(function (LicenseSeat $seat) {
+6 -2
View File
@@ -5,6 +5,7 @@ namespace Database\Factories;
use App\Models\Asset;
use App\Models\Maintenance;
use App\Models\Supplier;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class MaintenanceFactory extends Factory
@@ -24,14 +25,17 @@ class MaintenanceFactory extends Factory
public function definition()
{
return [
'asset_id' => Asset::factory(),
'asset_id' => Asset::factory()->laptopZenbook(),
'supplier_id' => Supplier::factory(),
'asset_maintenance_type' => $this->faker->randomElement(['maintenance', 'repair', 'upgrade']),
'name' => $this->faker->sentence(),
'name' => $this->faker->name(),
'start_date' => $this->faker->date(),
'is_warranty' => $this->faker->boolean(),
'notes' => $this->faker->paragraph(),
'url' => $this->faker->url(),
'cost' => $this->faker->randomFloat(),
'created_by' => User::factory()->superuser(),
'image' => $this->faker->numberBetween(1,11).'.png',
];
}
}
@@ -20,6 +20,21 @@ class ReportTemplateFactory extends Factory
'id' => '1',
],
'created_by' => User::factory(),
'is_shared' => 0,
];
}
public function shared()
{
return $this->state(function () {
return['is_shared' => 1];
});
}
public function notShared()
{
return $this->state(function () {
return ['is_shared' => 0];
});
}
}
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('report_templates', function (Blueprint $table) {
$table->boolean('is_shared', )->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('report_templates', function (Blueprint $table) {
if (Schema::hasColumn('report_templates', 'is_shared')) {
$table->dropColumn('is_shared');
}
});
}
};
+1 -1
View File
@@ -44,6 +44,7 @@ class DatabaseSeeder extends Seeder
$this->call(ComponentSeeder::class);
$this->call(ConsumableSeeder::class);
$this->call(ActionlogSeeder::class);
$this->call(MaintenanceSeeder::class);
Artisan::call('snipeit:sync-asset-locations', ['--output' => 'all']);
@@ -53,7 +54,6 @@ class DatabaseSeeder extends Seeder
Model::reguard();
DB::table('imports')->truncate();
DB::table('maintenances')->truncate();
DB::table('requested_assets')->truncate();
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace Database\Seeders;
use App\Models\Maintenance;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class MaintenanceSeeder extends Seeder
{
public function run()
{
Maintenance::truncate();
Maintenance::factory()->create(['image' => '1.png']);
Maintenance::factory()->create(['image' => '2.png']);
Maintenance::factory()->create(['image' => '3.png']);
Maintenance::factory()->create(['image' => '4.png']);
Maintenance::factory()->create(['image' => '5.png']);
Maintenance::factory()->create(['image' => '6.png']);
Maintenance::factory()->create(['image' => '7.png']);
Maintenance::factory()->create(['image' => '8.png']);
Maintenance::factory()->create(['image' => '9.png']);
Maintenance::factory()->create(['image' => '10.png']);
Maintenance::factory()->create(['image' => '11.png']);
$src = public_path('/img/demo/maintenances/');
$dst = 'maintenances'.'/';
$del_files = Storage::files($dst);
foreach ($del_files as $del_file) { // iterate files
$file_to_delete = str_replace($src, '', $del_file);
Log::debug('Deleting: '.$file_to_delete);
try {
Storage::disk('public')->delete($dst.$del_file);
} catch (\Exception $e) {
Log::debug($e);
}
}
$add_files = glob($src.'/*.*');
foreach ($add_files as $add_file) {
$file_to_copy = str_replace($src, '', $add_file);
Log::debug('Copying: '.$file_to_copy);
try {
Storage::disk('public')->put($dst.$file_to_copy, file_get_contents($src.$file_to_copy));
} catch (\Exception $e) {
Log::debug($e);
}
}
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('Access denied.');
$icon = '';
$files = [
'storage/oauth-private.key' => '660',
'storage/oauth-public.key' => '660',
];
echo "\n";
// Normalize key permissions for Passport 13 (covers both fresh installs and upgrades)
if (PHP_OS_FAMILY !== 'Windows') {
foreach ($files as $file => $permission) {
if (file_exists($file)) {
try {
@chmod($file, $permission);
$messages[]['success'] = "Permissions updated to ".$permission." on ".$file." \n";
} catch (Exception $e) {
$messages[]['error'] = "Could not change permissions for ".$file.". Please manually change the permissions on this file to ".$permission.". See the documentation: https://snipe-it.readme.io/docs/debugging-permissions#linuxosx \n";
}
} else {
$messages[]['info'] = "The file ".$file." was not found and may not have been created yet. \n";
}
}
if (count($messages) > 0) {
for($x = 0; $x < count($messages); $x++) {
foreach ($messages[$x] as $type => $message) {
if ($type === 'error') {
echo " \e[0;97;41m ERROR \e[0m ";
} elseif ($type === 'info') {
echo " \e[0;97;44m INFO \e[0m ";
} elseif ($type === 'success') {
echo " \e[0;97;42m SUCCESS \e[0m ";
}
echo $message;
}
}
}
echo "\n";
exit();
}
echo " \e[0;97;44m INFO \e[0m Windows OS detected, so OAuth key permissions could not be set. If you have problems with API calls or tables loading in your Snipe-IT application, see the documentation on how to correct them: https://snipe-it.readme.io/docs/debugging-permissions#windows \n";
exit();
+4 -4
View File
@@ -6816,11 +6816,11 @@
}
},
"node_modules/jspdf-autotable": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.2.tgz",
"integrity": "sha512-YNKeB7qmx3pxOLcNeoqAv3qTS7KuvVwkFe5AduCawpop3NOkBUtqDToxNc225MlNecxT4kP2Zy3z/y/yvGdXUQ==",
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.7.tgz",
"integrity": "sha512-2wr7H6liNDBYNwt25hMQwXkEWFOEopgKIvR1Eukuw6Zmprm/ZcnmLTQEjW7Xx3FCbD3v7pflLcnMAv/h1jFDQw==",
"peerDependencies": {
"jspdf": "^2 || ^3"
"jspdf": "^2 || ^3 || ^4"
}
},
"node_modules/jspdf/node_modules/core-js": {
Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

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