Compare commits

...

353 Commits

Author SHA1 Message Date
victoria aba3a0733d Update accessories-sample.csv 2026-02-12 15:16:06 -10:00
snipe 929132ba07 Merge pull request #18544 from uberbrady/fix_1001_query_on_available_models_for_notification
Added some withCount() params to tweak the 'minimum number of assets' notification
2026-02-12 21:37:10 +00:00
Brady Wetherington 2a1a4a1c72 Added some withCount() params to tweak the 'minimum number of assets' notification 2026-02-12 21:32:30 +00:00
snipe 4d73894cd2 Merge pull request #18542 from marcusmoore/fixes/fd-53577-reminder-subject-2
Updated subject line of acceptance reminder emails
2026-02-12 21:22:55 +00:00
snipe e49e805b2b Merge pull request #18539 from Godmartinz/L7163_title_adjustment
Fixes title alignment to fit label L7163_A
2026-02-12 20:43:52 +00:00
snipe 05f3bf633e Fixed typeError for isManagerOf in manager view of EULAs 2026-02-12 20:21:15 +00:00
Marcus Moore 4d5b3548d6 Update introduction line 2026-02-11 16:28:17 -08:00
Marcus Moore 00408f0103 Revert "Put checkout_qty on checkoutable so accessory and consumable emails are formatted correctly"
This reverts commit 548cceee18.
2026-02-11 16:06:27 -08:00
Marcus Moore 548cceee18 Put checkout_qty on checkoutable so accessory and consumable emails are formatted correctly 2026-02-11 13:39:37 -08:00
Godfrey M 76d1a20e21 adjust label L7163_A to fit title properly 2026-02-11 11:02:49 -08:00
snipe 30970cc7f2 A few more disabled/readonly state color fixes 2026-02-11 11:21:53 +00:00
snipe 985c027b04 Fixed disabled select2 color 2026-02-11 11:12:33 +00:00
snipe d1c4b055a9 Merge pull request #18535 from grokability/rename-field-in-infobox
Update the name of the object within the side panel
2026-02-11 10:20:23 +00:00
snipe 3f2f508e49 Fixed failing test 2026-02-11 10:16:44 +00:00
snipe 07513cb559 Update the name of the object within the side panel
It’s not always a contact/person, so…
2026-02-11 10:02:20 +00:00
snipe 66022902b7 Small style tweaks to info-box 2026-02-11 09:57:43 +00:00
snipe 613efe963a Use consistent box icon 2026-02-11 09:48:44 +00:00
snipe 5a0affcd8e Updated categories view 2026-02-11 09:47:38 +00:00
snipe 79150edb92 Fixed variable name 2026-02-11 07:41:13 +00:00
snipe 6cca24be8e Fixed typo 2026-02-11 07:27:16 +00:00
snipe c2ca51e8ef Added edit buttons to supplier view 2026-02-11 07:27:08 +00:00
snipe b9908d5665 Cast the DB_PORT to integer 2026-02-11 07:01:03 +00:00
snipe cbca420217 Added tooltips 2026-02-11 06:42:15 +00:00
snipe 017183e3fe Merge pull request #18529 from grokability/consolidate-customfieldset-edit
Consolidated custom fieldset edit/show
2026-02-11 05:25:27 +00:00
snipe 0236527f05 Use correct seeding color for links 2026-02-10 15:27:05 +00:00
snipe e946c4bf8c Consolidated custom fieldset edit/show 2026-02-10 15:20:57 +00:00
snipe 8a0414cef6 Merge remote-tracking branch 'origin/master' into develop 2026-02-10 14:16:42 +00:00
snipe ee1ad692a4 Tweaked disabled button color 2026-02-10 13:40:14 +00:00
snipe fdd5f6b0e1 Fixed #18497 - Added model name to aliases 2026-02-10 13:07:41 +00:00
snipe dca6df3244 Fixed #18527 - disable sticky column on dashboard tables 2026-02-10 11:31:25 +00:00
snipe 0874b853a0 Fixed #18520 - use plain_text_company 2026-02-10 11:10:48 +00:00
snipe 3393916b5e Merge pull request #18524 from ubc-cpsc/fix/subpath-livewire-prefix
Fix Livewire and Passport routes for subpath hosting
2026-02-10 08:34:18 +00:00
Joël Pittet b2728b4eb1 Fix Livewire routes for subpath hosting 2026-02-10 00:22:13 -08:00
snipe f3cc3ed682 Merge pull request #18470 from bilias/pr-clean
Do not delete asset name if update request does not have a name
2026-02-09 20:57:07 +00:00
snipe f8cfb8833f Merge pull request #18514 from grokability/update-models-with-new-components
Update models with new components
2026-02-09 19:53:40 +00:00
snipe c2c2332e83 Removed erroneous icon 2026-02-09 13:05:22 +00:00
snipe 9f69c36426 Added depreciations and departments 2026-02-09 13:03:07 +00:00
snipe ea9de35a3b Show/hide on companies and depts 2026-02-07 18:44:37 +00:00
snipe a50a16fb01 Nicer toggle, nicer show/hide info button 2026-02-07 18:39:42 +00:00
snipe f27c0206de Expand/contract info tab (still looks a little junky) 2026-02-07 17:52:22 +00:00
snipe 6791ddd911 Merge remote-tracking branch 'origin/develop' 2026-02-07 15:55:20 +00:00
snipe ce95060d60 Fixed #18516 - added kits for sticky columns 2026-02-07 15:55:11 +00:00
snipe 6d9bbe1ddf Small tweaks to EULA API 2026-02-07 15:41:51 +00:00
snipe 5b1507f4b7 Updated license presenter 2026-02-07 15:16:32 +00:00
snipe 53e985aaab Tweaked color for alt striping again 2026-02-07 15:08:51 +00:00
snipe f5c2119122 Merge remote-tracking branch 'origin/develop' 2026-02-07 15:03:58 +00:00
snipe 5fd6918948 Fixed text color in light mode on alt striping 2026-02-07 15:03:50 +00:00
Marcus Moore 7423d13bdd Accept $firstTimeSending in mails 2026-02-05 17:06:36 -08:00
Marcus Moore fb8586f186 Properly pass parameter for asset emails 2026-02-05 16:59:49 -08:00
Marcus Moore 8b9ebf736b Add assertions for subject line 2026-02-05 16:59:36 -08:00
Marcus Moore 5abcdb8c5a Fix relationships in AccessoryCheckout model 2026-02-05 16:46:51 -08:00
Marcus Moore e549f67fcc Create AccessoryCheckout factory 2026-02-05 16:45:25 -08:00
Marcus Moore d68576c2fa Make test name more accurate 2026-02-05 13:57:51 -08:00
Marcus Moore a6042b6a03 Clean up 2026-02-05 13:52:15 -08:00
Marcus Moore d1a3afd992 More test clean up 2026-02-05 13:39:47 -08:00
Marcus Moore 34b00f7dba More test clean up 2026-02-05 13:38:05 -08:00
Marcus Moore 14b6c6861f Test clean up 2026-02-05 13:34:09 -08:00
Marcus Moore fe4bb4209c Split out tests 2026-02-05 13:07:41 -08:00
Marcus Moore e997eb2012 Inline method 2026-02-05 12:57:42 -08:00
snipe 8337473f5a Added box arrows 2026-02-05 19:27:36 +00:00
snipe 1f50eada6d Added bulk edit to department user listing 2026-02-05 14:38:46 +00:00
snipe 38e1114dad Convert manufacturers and departments 2026-02-05 14:35:01 +00:00
snipe 11be73a578 Removed stray closing div 2026-02-05 14:14:25 +00:00
snipe 13a00df73c Use new components for company view 2026-02-05 14:13:51 +00:00
snipe 7739690bf5 Extend SnipeModel for custom fieldsets 2026-02-05 13:41:26 +00:00
snipe 389eb9e05d Bumped hash 2026-02-05 13:25:53 +00:00
snipe 98fe94aa24 Bumped hash 2026-02-05 13:25:21 +00:00
snipe 4ee378bf8e Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	public/css/dist/bootstrap-table.css
#	public/js/dist/bootstrap-table.js
#	resources/views/blade/table/index.blade.php
#	resources/views/models/custom_fields_form.blade.php
2026-02-05 13:24:27 +00:00
snipe a8c268760b Fixed #18500 - skip encoding on API url for table component 2026-02-05 13:19:16 +00:00
snipe c684b8ab1e Fixed #18502 - custom fields disappearing on smaller resolution breakpoints 2026-02-05 12:25:49 +00:00
snipe 333d0e2391 Fixed #18510 - use correct GH link in social footer 2026-02-05 11:28:29 +00:00
snipe c22c5993fc Fixed #18503 - alt row colors on striped tables 2026-02-05 11:27:10 +00:00
snipe 1c239cc7cf Fixed typo in icon name 2026-02-05 10:16:24 +00:00
snipe 6ee5aa3e12 Fixed checkout to all modal 2026-02-04 20:55:38 +00:00
snipe 0d5ceb1e90 Merge pull request #18492 from marcusmoore/bump-phpunit
Bumped PHPUnit version
2026-02-04 20:48:06 +00:00
snipe 166a241761 Merge pull request #18484 from spencerrlongg/undefined_var_files
Fixes `Undefined variable $files` Exception
2026-02-04 20:47:02 +00:00
snipe f6f5e806e4 More flexibility in blade components for labels vs icons vs icon types 2026-02-04 20:31:11 +00:00
snipe f0f42240f3 Still more icons 2026-02-04 20:30:20 +00:00
snipe 686afc0974 Gate check linking in presenters 2026-02-04 20:30:12 +00:00
snipe 7e5fb3c9ae Added more icons 2026-02-02 15:20:21 +00:00
snipe 65e4c0b8d1 Renamed contact box to info-panel 2026-02-02 12:22:20 +00:00
snipe b1301237f1 Removed double lines in table 2026-02-02 11:35:09 +00:00
snipe cf82c12708 Fixed custom fieldset table colors 2026-02-02 11:27:18 +00:00
snipe 52f8697d91 Added icons, more additions to sidebar 2026-02-02 11:16:53 +00:00
Marcus Moore 3169c5b503 Bump phpunit 2026-01-28 10:15:11 -08:00
snipe 741f0d69ab Merge pull request #18490 from Godmartinz/add-label-preview-translation
Add translation and template name for Label Preview
2026-01-28 13:14:54 +00:00
Godfrey M 53b283eac5 add seperate brackets for template name 2026-01-27 16:21:06 -08:00
Godfrey M 1234b6297e add Label preview translation, also which label is previewed 2026-01-27 14:59:44 -08:00
spencerrlongg 8ff34baafa wrap $files in isset to avoid null errors 2026-01-26 15:44:23 -06:00
snipe eeae534a37 Added a few more helpers 2026-01-25 14:33:54 +00:00
snipe 8b71049a3d Merge pull request #18482 from grokability/move-form-components
Move form components into their own directory
2026-01-24 21:13:07 +00:00
snipe 373361dab0 Move form components into their own directory 2026-01-24 21:06:02 +00:00
snipe 60a1141b9d Merge pull request #18480 from grokability/#more-table-components
Moves more indexes to blade components
2026-01-24 20:55:51 +00:00
snipe a38c8a0235 Merge more into contact card 2026-01-24 20:37:08 +00:00
snipe c39d165a3d Added space 2026-01-24 20:36:50 +00:00
snipe 2238f8e8ad More icons 2026-01-24 20:36:43 +00:00
snipe e0c53c3ead Merge classes 2026-01-24 18:32:13 +00:00
snipe 1074bc2d3b More conversions 2026-01-24 18:27:06 +00:00
snipe 4b22b1c115 Better defaults in nav-item component 2026-01-24 17:41:02 +00:00
snipe 141794caf7 Use new contact panel in suppliers view 2026-01-24 17:40:41 +00:00
snipe d5997e2394 Added new classes 2026-01-24 17:40:29 +00:00
snipe 429ca8dd34 Added info components 2026-01-24 17:40:18 +00:00
snipe c2ad0defe0 Added contact component 2026-01-24 17:39:53 +00:00
snipe b4658f2696 Added address presenter 2026-01-24 17:39:44 +00:00
snipe 11b7dfc9b0 Added icons 2026-01-24 17:39:33 +00:00
snipe f7b7ef850d Updated suppliers view 2026-01-24 12:10:58 +00:00
snipe bb9b145519 Updated helper icon 2026-01-24 12:10:50 +00:00
snipe d75f5b8fd3 Fixed label 2026-01-23 19:14:29 +00:00
snipe 34fe64b27c Fixed spacing 2026-01-23 19:09:48 +00:00
snipe 2151595b45 Fixed a few typos 2026-01-23 18:28:49 +00:00
snipe b2c94386b3 Switched from stupid layouts/edit-form to new form component 2026-01-23 18:26:09 +00:00
snipe b99ae9be88 Form component (will move the other things later) 2026-01-23 18:25:49 +00:00
snipe ffbc831071 Include the box footer if there is a route passed 2026-01-23 18:25:34 +00:00
snipe 302a60fbe7 Better breadcrumb 2026-01-23 18:25:06 +00:00
snipe 3ec8ba01d0 Updated manufacturer with new tabs 2026-01-23 18:24:50 +00:00
snipe 2048eb7d5a Removed unneeded presenter methods 2026-01-23 17:08:25 +00:00
snipe 4d605927f3 Fixed typos 2026-01-23 17:07:38 +00:00
snipe b7ee3a2f1d Updated groups index to use new components 2026-01-23 17:05:28 +00:00
snipe 44fa9e2d1e Handle status label index 2026-01-23 16:58:42 +00:00
snipe a5e818f970 More moved to new box+table method 2026-01-23 16:47:27 +00:00
snipe 066cf81233 Added model bulk component 2026-01-23 16:46:52 +00:00
snipe 1b4552cf30 Refactorered bulk action component 2026-01-23 16:46:42 +00:00
snipe 0ba8ee1c5f Added bulkactions named slot in box index 2026-01-23 16:45:48 +00:00
snipe 5c4f3a5aec Removed unneeded body blade 2026-01-23 16:45:30 +00:00
snipe c963c0b25b Updated path for new table component location 2026-01-23 15:45:36 +00:00
snipe b3a4fb2676 Use new user bulk edit blade 2026-01-23 15:45:19 +00:00
snipe 0b82902248 Fixed background loading color 2026-01-23 15:45:06 +00:00
snipe e7f70ccd1f Moved to new component structure 2026-01-23 15:44:52 +00:00
snipe 683395599f Moved files into table directory 2026-01-23 15:44:12 +00:00
snipe 755d7c2351 Added printIgnore, reordered fields 2026-01-23 15:43:35 +00:00
snipe 17d91bcd8e Merge pull request #18479 from grokability/improved-tab-components
Removed font-size override on tabs
2026-01-23 13:53:33 +00:00
snipe 2633ec10dc Removed font-size override on tabs 2026-01-23 13:52:00 +00:00
snipe c4f772c8d9 Merge pull request #18478 from grokability/refined-sticky-columns
Added sticky column
2026-01-23 13:50:49 +00:00
snipe 18addf2a87 Added sticky column 2026-01-23 13:49:09 +00:00
snipe eee826248d Merge pull request #18476 from marcusmoore/fixes/18475-action-log-checkin-during-import
Fixed #18475 - reference the correct model when checking in an asset via import
2026-01-23 09:37:50 +00:00
Marcus Moore 96a817753c Pass the assigned model to the event instead of always passing a User 2026-01-22 14:24:37 -08:00
snipe 600b06e66b Merge pull request #18474 from grokability/move-box-index
Renamed box blade component wrapper to index
2026-01-22 21:10:53 +00:00
snipe cd680daa4c Merge remote-tracking branch 'origin/develop' 2026-01-22 20:58:42 +00:00
snipe 315b716e87 Removed advanced search from files table
It’s not wired up on the backend, so…
2026-01-22 20:50:24 +00:00
snipe b2f1966a78 Renamed box blade component wrapper to index 2026-01-22 20:41:25 +00:00
snipe 6305f87037 Merge pull request #18473 from grokability/added-tab-component
Added tab components
2026-01-22 20:37:11 +00:00
snipe ab02b67d3c Merge remote-tracking branch 'origin/develop' 2026-01-22 20:34:51 +00:00
snipe 0f4086eaf0 Added gates to the tab panes as well 2026-01-22 20:32:33 +00:00
snipe e7c5329e57 Merge pull request #18472 from Godmartinz/fix-n+1-issue
Moved helper query, to eager load through Asset API index
2026-01-22 20:27:44 +00:00
snipe ad82ea86c8 Added tab components 2026-01-22 20:25:33 +00:00
Godfrey M c310e1e3b7 update blade 2026-01-22 11:37:24 -08:00
Godfrey M aaf9372474 move helper query to eager load in the asset api 2026-01-22 11:12:49 -08:00
snipe 2cf169359e Missed updating the closing tags 2026-01-22 15:45:53 +00:00
snipe 7f67f8c20d Merge pull request #18471 from grokability/move-box-blades-into-own-directory
Moved anonymous box blade components into their own directory
2026-01-22 15:44:10 +00:00
snipe 5607dfcecb Moved anonymous box blade components into their own dir 2026-01-22 15:38:20 +00:00
Giannis Kapetanakis ca99f525c9 Do not delete asset name if update request does not have a name 2026-01-22 12:40:59 +02:00
snipe 7e92517d13 Merge remote-tracking branch 'origin/develop' 2026-01-22 08:46:46 +00:00
snipe 1ebb67b2e7 Nicer route formatting for maintenances 2026-01-22 08:45:38 +00:00
snipe d9ef7b43b0 Merge remote-tracking branch 'origin/develop' 2026-01-21 19:59:59 +00:00
snipe c074fae885 Merge pull request #18459 from Godmartinz/moar_label_fixes
Fixes #18353 Label title scales with label fields. adjusted label row allotment.
2026-01-21 19:58:32 +00:00
snipe c5ada0fc2f Merge pull request #18466 from marcusmoore/eager-load-expiring-alerts-command
Added eager load to Expiring Alerts command
2026-01-21 19:57:44 +00:00
snipe 5268b0f67f Merge pull request #18456 from grokability/container-component-phase-2
Next step in container+box component
2026-01-21 19:57:32 +00:00
Marcus Moore 7e10089c13 Eager load assignedTo and supplier 2026-01-21 11:49:02 -08:00
Godfrey M 1dab36da2d replaced extracted variables with array 2026-01-21 10:32:26 -08:00
snipe fab50e53b8 Merge pull request #18458 from marcusmoore/fixes/17309-asset-eol-in-custom-report
Fixed #17309 - include EOL date in custom asset report
2026-01-21 11:45:08 +00:00
Godfrey M 10cfe6d37a remove duplicate parameter 2026-01-20 16:23:36 -08:00
Godfrey M 3718ce9749 if label is null make room for value 2026-01-20 16:18:13 -08:00
Godfrey M fd39c8bf11 update title help text 2026-01-20 16:09:04 -08:00
Godfrey M 2e122fa8d8 update LW 11354 2026-01-20 16:00:56 -08:00
Godfrey M 66d85d17d9 update LW 1933081 2026-01-20 16:00:24 -08:00
Godfrey M 61bc570d59 add title to layout helper, update lw2112283 2026-01-20 15:59:40 -08:00
Marcus Moore 62dbd400a4 Use asset_eol_date in custom asset report 2026-01-20 15:07:19 -08:00
snipe 74af52d29d Merge pull request #18457 from marcusmoore/fixes/api-image-upload
Fixed storing image for accessory and consumable creation via api
2026-01-20 22:28:04 +00:00
Marcus Moore 59f377b058 Prepare images prior to validation in accessory and asset creation requests 2026-01-20 13:38:01 -08:00
snipe 949f65b210 Merge pull request #18356 from Godmartinz/add-first-checkout-to-asset
Adds #17210 1st checkout to asset view and index
2026-01-20 20:36:21 +00:00
snipe 4046fbae89 Merge pull request #18256 from iryadifarhan/fix/audit-log-displays-negative-number-incorrectly-when-next-audit-date-filled-with-current-date
Fixes inconsistent negative symbol at Audit Log Report when Next Audit Date is set to the current date
2026-01-20 20:35:16 +00:00
snipe da8776c2f1 Larger group select box 2026-01-20 20:20:39 +00:00
snipe 4043df1d02 Sort groups by name, asc 2026-01-20 20:13:22 +00:00
snipe 3bc5ab593a Next step in container+box component 2026-01-20 14:49:09 +00:00
snipe 6dad0d669f Merge pull request #18454 from grokability/container-components
Use basic container+box for primary index pages with one column
2026-01-19 16:54:45 +00:00
snipe 39f581a826 Use basic container+box for primary index pages with one column 2026-01-19 16:47:55 +00:00
snipe 2034b25b25 Set footer to default false 2026-01-19 16:20:48 +00:00
snipe 468a7aa911 Added button component 2026-01-19 16:19:07 +00:00
snipe ec50643d96 Basic container+box components 2026-01-19 16:17:47 +00:00
snipe 3b98fb666f Added tel as form type to make look right 2026-01-19 16:15:14 +00:00
snipe f17eeed579 Added fax icon 2026-01-19 15:09:28 +00:00
snipe 452d0b6f6e Merge remote-tracking branch 'origin/develop' 2026-01-19 11:44:04 +00:00
snipe 41450b6e1b Merge pull request #18368 from marcusmoore/feature/group-create-edit-load-improvement
Improved loading speeds on group create and edit page
2026-01-19 11:38:00 +00:00
snipe 30029462b1 Merge remote-tracking branch 'origin/develop' 2026-01-17 11:40:22 +00:00
snipe e109879cac Small autolabeler improvements 2026-01-17 11:40:08 +00:00
snipe 90ad4e9abf Merge remote-tracking branch 'origin/develop' 2026-01-17 11:27:43 +00:00
snipe fd0e2b1a96 Fixed indenting and page footer 2026-01-17 11:27:34 +00:00
snipe 19d637efea Merge remote-tracking branch 'origin/develop' 2026-01-16 13:03:26 +00:00
snipe 6c18b35276 Added barcode class 2026-01-16 13:03:17 +00:00
snipe 184eddb5cf Merge remote-tracking branch 'origin/develop' 2026-01-16 12:52:43 +00:00
snipe 1d577b0171 Merge pull request #18450 from grokability/#18449-small-sidenav-fixes
Fixed #18449 - small sidenav improvements for selected contexts
2026-01-16 12:51:52 +00:00
snipe 05c998227a Fixed #18449 - small sidenav improvements for selected contexts 2026-01-16 12:48:28 +00:00
snipe 53ed810d86 Merge remote-tracking branch 'origin/develop' 2026-01-16 11:38:09 +00:00
snipe 7c136b6f57 Use fa-fw in footer icons 2026-01-16 11:37:57 +00:00
snipe 4e510d9d8c Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	resources/views/models/index.blade.php
2026-01-16 11:36:35 +00:00
snipe e1f30f96c9 Remove stray closing divs 2026-01-16 11:36:10 +00:00
snipe 146d0cefc0 Removed extra closing divs 2026-01-16 11:35:18 +00:00
snipe 0103bd58e1 Merge pull request #18434 from Godmartinz/label_lay_fix
Fixes Label text scaling
2026-01-15 21:13:01 +00:00
Godfrey M dbc1e7d6ab remove scaling from label title 2026-01-15 12:03:45 -08:00
snipe ea706863b5 Merge remote-tracking branch 'origin/develop' 2026-01-15 11:42:18 +00:00
snipe 04f4f5b57c Merge pull request #18439 from grokability/#18135-better-handle-cli-importer-permissions
Fixed #18135 - only unset sensitive variables in the web UI importer
2026-01-15 11:39:03 +00:00
snipe 98b9246c10 Merge pull request #18442 from uberbrady/fewer_scim_exceptions
Fixed - throw fewer exceptions on SCIM misconfigurations
2026-01-15 11:38:49 +00:00
snipe 1e158721ee Merge remote-tracking branch 'origin/develop' 2026-01-15 11:10:09 +00:00
snipe ab4eefcac2 Added links to snipeit-mcp (by @jameshgordy) and SnipeScheduler (by @JSY-Ben) 2026-01-15 11:09:50 +00:00
snipe cf3b36f124 Merge remote-tracking branch 'origin/develop' 2026-01-15 09:45:40 +00:00
snipe 8da01b8569 Check for ID in ldap sync UI screen 2026-01-15 09:45:29 +00:00
snipe c90afaa312 Fixed quotes in cli purge eula command 2026-01-15 09:10:31 +00:00
snipe 9f20bb89c1 Merge remote-tracking branch 'origin/develop' 2026-01-14 21:37:54 +00:00
snipe dfe664b779 Merge pull request #18437 from grokability/#18409-purge-signature-pdfs
Fixed #18409 - command line option to purge signatures
2026-01-14 21:37:35 +00:00
snipe 88bd1fd6ef Merge pull request #18387 from vasiliyplotnikov/mysql_rds_ssl_support_18312
Fixed #18312: support aws rds mysql with force tls
2026-01-14 21:23:18 +00:00
snipe 33f8823edb Merge remote-tracking branch 'origin/develop' 2026-01-14 21:17:28 +00:00
snipe 06d5c0a86c Merge pull request #18440 from ManiacTwister/fix-importer-logfile
Fixed #8740: Use log instance with configured log path
2026-01-14 21:17:09 +00:00
snipe e12a5eda01 Merge remote-tracking branch 'origin/develop' 2026-01-14 20:56:44 +00:00
snipe b3c59b2cc3 Use readonly style with light/dark on LDAP settings 2026-01-14 20:56:35 +00:00
Brady Wetherington 24d5969ce8 Bump versions of our branch of laravel-scim-server to reduce reollbar usage 2026-01-14 14:37:47 +00:00
snipe 6ad42a02ad Merge remote-tracking branch 'origin/develop' 2026-01-14 14:21:50 +00:00
snipe 90b1ee4805 Check that signature exists on the server before trying to display 2026-01-14 14:21:41 +00:00
snipe 0c9d5ca9da Merge remote-tracking branch 'origin/develop' 2026-01-14 14:13:53 +00:00
snipe e05ecd0c3e Add created_by on acceptance logging to action_log 2026-01-14 14:13:43 +00:00
snipe 441657cbfb Merge remote-tracking branch 'origin/develop' 2026-01-14 13:48:54 +00:00
snipe 0fb6b02fc4 Merge pull request #18418 from Godmartinz/rb20479
Fix [RB-20479] Checkin/Checkout notifications from logging as errors
2026-01-14 13:37:48 +00:00
snipe f83df0d651 Merge remote-tracking branch 'origin/develop' 2026-01-14 13:34:29 +00:00
snipe e667cd20a8 Set require_signature to default to 0 2026-01-14 13:30:54 +00:00
ManiacTwister d06c56367f Fixed #8740: Use log instance with configured log path 2026-01-14 14:23:42 +01:00
snipe 039b4cf19f Fixed #18135 - only unset variables if the user is authenticated (Web UI) 2026-01-14 13:10:46 +00:00
snipe ca0961bd49 Clarified text 2026-01-14 12:50:57 +00:00
snipe 79528fa87b Fixed #18409 - command line option to purge signatures 2026-01-14 12:45:18 +00:00
Godfrey M d96d0b1bcb applied to LabelWriter_11354 2026-01-13 12:11:30 -08:00
Godfrey M 02c237404e applied to TZe_24mm_E 2026-01-13 12:07:39 -08:00
Godfrey M c56f5282ff applied to TZe_241 2026-01-13 12:02:05 -08:00
Godfrey M d62f10cb46 apply helper to L4736_A 2026-01-13 11:57:53 -08:00
Godfrey M 4c6f123cda apply helper to L6009_A 2026-01-13 11:55:10 -08:00
Godfrey M b0b1829426 add Helper function for label layout, applied to 1933081 2112283" 2026-01-13 11:50:14 -08:00
snipe 68b590c263 Merge pull request #18419 from Godmartinz/rb20638
Refactor the exceptions for Audit log (again)
2026-01-13 18:20:20 +00:00
Godfrey M b45efddd9a readd connection and throwable 2026-01-13 08:56:12 -08:00
snipe d8f5d8c6ec Merge remote-tracking branch 'origin/develop' 2026-01-13 13:34:18 +00:00
snipe 56baa8ac9f Fixed manager view label alignment 2026-01-13 13:34:09 +00:00
snipe 15aa64ed28 Merge remote-tracking branch 'origin/develop' 2026-01-13 13:30:52 +00:00
snipe 6d0bfeb420 Fixed #18422 - Hide edit buttons if the manager is looking at a different profile 2026-01-13 13:30:42 +00:00
snipe ceff334420 Merge remote-tracking branch 'origin/develop' 2026-01-13 12:43:25 +00:00
snipe 4ee2db68fc Fixed #18429 - corrected string for requestable vs requested items 2026-01-13 12:43:07 +00:00
snipe 1f04d0023b Merge remote-tracking branch 'origin/develop' 2026-01-13 12:38:43 +00:00
snipe 4427fdcaec Use theme button for LDAP sync 2026-01-13 12:38:33 +00:00
snipe 4de642c6a4 Merge pull request #18430 from grokability/#18415-null-ldap-display-name
Fixed #18415 - LDAP sync improvements to allow null display_name and bulk editing display_name
2026-01-13 12:37:48 +00:00
snipe d3eb89a97c Fixed display name in user table 2026-01-13 12:01:34 +00:00
snipe d7362a3785 Nicer light/dark for ldap sync page 2026-01-13 12:01:12 +00:00
snipe 26347ac41e Add display name to bulk user edit 2026-01-13 12:00:46 +00:00
snipe b396da3f33 Use null instead of blank if value is empty 2026-01-13 12:00:27 +00:00
snipe 306a0bf6de Merge remote-tracking branch 'origin/develop' 2026-01-13 10:22:38 +00:00
snipe 8000e274c6 Fixed #18424 - adds BYOD to view-assets page 2026-01-13 10:22:19 +00:00
Godfrey M e48c40e5af remove use statements 2026-01-08 15:58:15 -08:00
Godfrey M df51318fb9 remove throwable exception..too vague 2026-01-08 15:55:30 -08:00
Godfrey M 0327d01287 typo fixes" 2026-01-08 10:52:27 -08:00
Godfrey M 0ab206ca13 log warnings instead of errors for 4xx status codes 2026-01-08 09:47:35 -08:00
snipe 542cdef0bd Merge remote-tracking branch 'origin/develop' 2026-01-07 15:55:44 +00:00
snipe f5955e14ff Merge pull request #18410 from grokability/#18402-saml-fields-readonly-display
Fixed #18402 - Clean up SAML readonly display
2026-01-07 15:55:25 +00:00
snipe a0df7adbd1 Override readonly styles on user edit page 2026-01-07 15:50:01 +00:00
snipe 82a4398ef6 Removed @disabled on textarea 2026-01-07 15:37:05 +00:00
snipe 9271930ba8 Clean up SAML readonly display 2026-01-07 15:27:33 +00:00
snipe f149f0d994 Merge remote-tracking branch 'origin/develop' 2026-01-07 13:36:31 +00:00
snipe 14ff325608 Merge pull request #18404 from kenchan0130/patch-displayname
fixed: Prefer display name of user on UI
2026-01-07 13:29:56 +00:00
Tadayuki Onishi 8e37fcc71e Since displayname should be referenced in the UI, we've changed it to use displayname 2026-01-07 21:59:27 +09:00
snipe bf910bc708 Merge pull request #18406 from dbakan/patch-1
Fixed #18405: Clean up closing divs to fix footer position
2026-01-07 12:55:54 +00:00
Daniel Albertsen 1136ea0779 Clean up stray closing div tags
Removed redundant closing div tags in the edit view.
2026-01-07 12:43:03 +01:00
Daniel Albertsen 9ad94b6562 Fix white space indention 2026-01-07 12:43:03 +01:00
snipe 5ddb0b4a55 Merge remote-tracking branch 'origin/develop' 2026-01-05 22:29:17 +00:00
snipe 990858ba41 Merge pull request #18398 from grokability/fixes-rb-#20282-set-files-variable
Set `$files` in UploadedFilesController API POST endpoint only if there are results
2026-01-05 22:28:48 +00:00
snipe 406951fc84 Set $files only if there are results 2026-01-05 22:17:06 +00:00
snipe 41c7bf4aaa Merge pull request #18397 from Godmartinz/rb-20474-fix
Adds [RB-20474] a try/catch to ms teams audit notification
2026-01-05 20:39:42 +00:00
Godfrey M 1543634cb0 remove line break 2026-01-05 12:39:28 -08:00
Godfrey M b935752ec0 reference endpoint appropriately" 2026-01-05 12:35:20 -08:00
Godfrey M 2a60b7b7b2 move endpoint 2026-01-05 12:31:00 -08:00
snipe cef78687b3 Merge remote-tracking branch 'origin/develop' 2026-01-05 20:27:37 +00:00
snipe 2f29edc01f Changed “reset to default” button to theme button style 2026-01-05 20:27:25 +00:00
snipe 134491b59e Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/mix-manifest.json
2026-01-05 20:20:51 +00:00
snipe 812ff0bfd2 Bumped jspdf to >=4.0.0 2026-01-05 20:20:09 +00:00
Godfrey M 201c4fa0d9 remove use path 2026-01-05 12:15:37 -08:00
Godfrey M ced83b9bfc add a try catch to ms teams audit notification 2026-01-05 12:14:42 -08:00
snipe e67c5273d7 Merge remote-tracking branch 'origin/develop' 2026-01-05 17:22:50 +00:00
snipe b4b9339065 Merge pull request #18396 from uberbrady/fix_select2_break_on_modal
Fixed #17652 - don't break company drop-down with modals
2026-01-05 17:22:29 +00:00
snipe f3cd68eb3f Merge remote-tracking branch 'origin/develop' 2026-01-05 17:21:44 +00:00
snipe ad57dea0e5 Suppress refresh button on client-side tables
Refresh only works on server-side tables
2026-01-05 17:21:32 +00:00
snipe 1e0a348b8e Merge remote-tracking branch 'origin/develop' 2026-01-05 17:19:56 +00:00
snipe 9c06a2126d Update sha.js to 2.4.12 2026-01-05 17:19:43 +00:00
snipe dab030e95d Merge remote-tracking branch 'origin/develop' 2026-01-05 16:56:02 +00:00
snipe 4995bc0d0d Small tweaks to select2 values 2026-01-05 16:55:53 +00:00
Brady Wetherington d2369893c8 Remove ID from company select in partial 2026-01-05 16:30:47 +00:00
snipe e48adf6443 Merge remote-tracking branch 'origin/develop' 2026-01-05 16:06:08 +00:00
snipe 505bca8386 Fixed :focus on theme buttons 2026-01-05 16:05:59 +00:00
snipe 31ca93a259 Merge remote-tracking branch 'origin/develop' 2026-01-05 15:40:59 +00:00
snipe 71523b7038 Merge pull request #18393 from grokability/#18291-bump-alpine
Attempted fix of #18291 - update PHP versions and alpine versions
2026-01-05 15:36:40 +00:00
snipe 9885dc9c8a Merge remote-tracking branch 'origin/develop' 2026-01-05 15:34:19 +00:00
snipe 00d9d9f132 Merge pull request #18395 from grokability/#18394-ldap-text-dark-mode
Fixed #18394 - LDAP sync test table background in dark mode
2026-01-05 15:34:00 +00:00
snipe 320dc3fac6 Fixed LDAP sync test table background in dark mode 2026-01-05 15:29:50 +00:00
Brady Wetherington 6e68e43a25 In this version of Alpine, php84 is default, so it's easier. 2026-01-05 15:11:08 +00:00
snipe a19c391aa1 Check can edit on demo in bulk user edit 2026-01-05 14:58:14 +00:00
snipe 32f0101c1b Attempted fix of #18291 - update PHP versions and alpine versions 2026-01-05 14:05:47 +00:00
snipe 12b9fdced5 Merge remote-tracking branch 'origin/develop' 2026-01-05 13:39:48 +00:00
snipe 0af45a53a9 Merge pull request #18359 from marcusmoore/17816-qty-in-activity-report
Fixed #17816 - added quantity to activity reports
2026-01-05 13:03:56 +00:00
snipe 37773d35f2 Merge pull request #18391 from ubc-cpsc/fix/PKSA-8x19-j2j3-bn67-sodium_compat
fix: paragonie/sodium_compat - Missing check that a point is on the prime subgroup for Edwards25519
2026-01-05 12:58:24 +00:00
snipe fca3eb4b7b Merge pull request #18392 from grokability/#18377-min-value-for-name
Fixes #18377 - make min value for names consistent
2026-01-05 12:56:29 +00:00
snipe 5e9e0b70db Fixes #18377 - make min value for names consistent 2026-01-05 12:51:49 +00:00
Joël Pittet 5242ffc04b fix: Missing check that a point is on the prime subgroup for Edwards25519 2026-01-02 13:55:18 -08:00
snipe f0c9a5b2dc Merge remote-tracking branch 'origin/develop' 2025-12-31 05:31:06 +00:00
snipe b3902e82fc Merge pull request #18388 from dylang3/fix/unintended-shell-exec-call
Fixed #18384: Remove backticks in bulk-actions.blade.php to avoid unintentional shell_exec() call
2025-12-31 05:30:06 +00:00
Dylan Guthrie d70eff6fc4 Fix: Remove backticks in bulk-actions.blade.php to avoid unintentional shell_exec() call 2025-12-30 22:37:49 -06:00
Vasily Plotnikov 5b5695ffe1 Fixed #18312: support aws rds mysql with force tls 2025-12-30 11:59:31 +00:00
snipe deb44cad30 Merge remote-tracking branch 'origin/develop' 2025-12-23 13:34:36 +00:00
snipe 35fdca3607 Added logo hover color 2025-12-23 13:33:53 +00:00
snipe 93080523d0 Merge remote-tracking branch 'origin/develop' 2025-12-21 08:34:42 +00:00
snipe b7b8f5a7e7 Merge pull request #18348 from grokability/dependabot/github_actions/develop/actions/cache-5
Bump actions/cache from 4 to 5
2025-12-21 08:16:48 +00:00
snipe 59ee55a6b2 Merge pull request #18349 from grokability/dependabot/github_actions/develop/actions/upload-artifact-6
Bump actions/upload-artifact from 5 to 6
2025-12-21 08:16:29 +00:00
snipe d85b25d683 Merge pull request #18363 from ubc-cpsc/task/request-get
fix: replace deprecated Symfony Request::get() usage
2025-12-21 08:09:50 +00:00
snipe 0108006fab Merge remote-tracking branch 'origin/develop' 2025-12-19 06:32:09 +00:00
snipe 755bb8f189 Merge pull request #18326 from dylang3/fix/users-groups-sync
Fixes #18325: Ensure existing permission group users are maintained when editing a group
2025-12-19 06:26:44 +00:00
Marcus Moore f53dcbc64f Only pull needed fields from database 2025-12-18 13:37:56 -08:00
Joël Pittet 0f215bbcf8 fix: replace deprecated Symfony Request::get() usage 2025-12-17 18:44:04 -08:00
snipe 9770775770 Merge remote-tracking branch 'origin/develop' 2025-12-17 16:27:42 +00:00
snipe 0b63bcc056 Derp. Copypasta 2025-12-17 16:25:03 +00:00
snipe 03116f5ece Fixed tests 2025-12-17 16:16:58 +00:00
snipe 5c091d8690 DIsable delete button if user cannot delete the user 2025-12-17 15:31:57 +00:00
snipe da1ca24190 Remove avatar delete - should be done in purge 2025-12-17 15:29:07 +00:00
Marcus Moore fa7382851f Use enum 2025-12-16 15:00:07 -08:00
Marcus Moore ab363596fd Replace qty with quantity 2025-12-16 14:55:00 -08:00
Marcus Moore b05970acf4 Add command to migrate license seat quantities in action log table 2025-12-16 13:48:23 -08:00
snipe aa6b70c296 Merge remote-tracking branch 'origin/develop' 2025-12-16 20:31:58 +00:00
snipe 00171e6d16 Fixed api docs url 2025-12-16 20:31:50 +00:00
snipe 061e0ded72 Merge remote-tracking branch 'origin/develop' 2025-12-16 20:26:18 +00:00
snipe 81bdd86fb7 Merge pull request #18358 from grokability/add-scim-url
Add SCIM url to admin settings
2025-12-16 20:06:50 +00:00
snipe 60ff06bcf0 Fixed nav footer label color 2025-12-16 20:02:20 +00:00
snipe a3e3f48d47 Added scim and API links to admin page 2025-12-16 19:59:52 +00:00
Godfrey M a3a79a696f adds first checkout to asset view and index 2025-12-16 08:48:08 -08:00
Marcus Moore 6947672046 Add seats to transformer 2025-12-15 17:31:56 -08:00
Marcus Moore 6f77e96998 Log quantity when adding or removing seats from license 2025-12-15 17:26:05 -08:00
Marcus Moore 8a595aa269 Display qty in history 2025-12-15 15:12:34 -08:00
Marcus Moore 1ccf38221f Set qty when accepting or declining checkout 2025-12-15 14:45:05 -08:00
Marcus Moore 18c639e6c0 WIP - adding qty to more event calls 2025-12-15 14:38:13 -08:00
Marcus Moore 06224371b3 Begin passing qty in CheckoutableCheckedOut event 2025-12-15 12:47:36 -08:00
dependabot[bot] d06105f410 Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 08:02:41 +00:00
dependabot[bot] 3226340b08 Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 08:02:30 +00:00
snipe 25ef5d64b4 Merge remote-tracking branch 'origin/develop' 2025-12-13 13:44:34 +00:00
snipe f286558065 Fixed #18344 2025-12-13 13:44:23 +00:00
snipe 9586c50cb5 Merge pull request #18347 from grokability/added-reset-for-theme
Added reset for theme, preview for effects in profile
2025-12-13 13:43:23 +00:00
snipe 4658bf38c5 Removed user migration for colors 2025-12-13 13:34:10 +00:00
snipe 951a4e37f3 New strings 2025-12-13 13:33:59 +00:00
snipe 6ad3154035 Added confetti, sounds test on checkboxes 2025-12-13 13:33:49 +00:00
snipe d7fa4a0df2 Tweaked colors 2025-12-13 13:33:13 +00:00
snipe 1112a40f0f Added reset button 2025-12-13 13:33:06 +00:00
Marcus Moore e908838376 Add failing tests 2025-12-10 15:44:51 -08:00
Marcus Moore 4b1339a11c Add qty to action_logs table 2025-12-10 13:30:34 -08:00
Dylan Guthrie c978be5cab send associated_users to view as collection 2025-12-09 19:47:42 -06:00
Dylan Guthrie 242201ca91 Fix: Ensure existing permission group users are maintained when editing group 2025-12-09 16:06:49 -06:00
iryadifarhan a3a49e47b7 Apply toDateString so that the it equally compare date only, evades including time/hour comparing 2025-11-26 13:21:15 +07:00
285 changed files with 132668 additions and 5189 deletions
+14
View File
@@ -40,12 +40,26 @@ DB_SANITIZE_BY_DEFAULT=false
# --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS
# --------------------------------------------
# Enable SSL connection to database (true/false)
DB_SSL=false
# Set to true for cloud databases like AWS RDS, Azure Database, Google Cloud SQL
# Set to false for self-hosted databases with client certificates
DB_SSL_IS_PAAS=false
# Required when DB_SSL_IS_PAAS=false (client certificate authentication)
DB_SSL_KEY_PATH=null
DB_SSL_CERT_PATH=null
# Path to CA certificate bundle (required for SSL connections)
# For AWS RDS, download from: https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
DB_SSL_CA_PATH=null
# SSL cipher (optional, leave null for default)
DB_SSL_CIPHER=null
# Verify server certificate (true/false, defaults to false if not set)
# Set to false for development or when using self-signed certificates
DB_SSL_VERIFY_SERVER=null
# --------------------------------------------
+4 -3
View File
@@ -1,10 +1,11 @@
frontend: ["*.js", "*.css", "*.vue", "*.scss", "*.less", "*.blade.*", "resources/views/livewire/*"]
frontend: ["*.js", "*.css", "*.scss", "*.less", "*.blade.*", "resources/views/livewire/*","resources/views/layouts/default.blade.php"]
skins: ["*.js", "*.css", "*.scss", "*.less"]
css: ["*.css","*.scss", "*.less"]
javascript: ["*.js", "package.json", "package.lock"]
backend: ["/app/*", "composer.json", "composer.lock"]
translations: ["/resources/lang"]
translations: ["/resources/lang/*"]
livewire: ["/app/Http/Livewire/*", "resources/views/livewire/*"]
blade-components: ["resources/views/blade/*"]
backups: ["*backup*"]
restore: ["*restore*"]
saml: ["*saml*"]
@@ -16,7 +17,7 @@ api: ["/app/Http/Controllers/Api/*"]
notifications: ["/app/Notifications/*"]
importer: ["/app/Importer/*","/app/Http/Livewire/Importer.php", "resources/views/livewire/importer.php"]
cli / artisan: ["/app/Console/*"]
LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php"]
LDAP: ["*Ldap*", "/app/Console/Commands/Ldap*","/app/Models/Ldap.php", "/resources/views/users/ldap.blade.php","/resources/views/settings/ldap.blade.php"]
docker: ["*docker/*", "Dockerfile", "Dockerfile.alpine", "Dockerfile.fpm-alpine", ".dockerignore", ".env.docker"]
tests: ["/tests/*", "/database/factories/*", "/stubs"]
config: .github
+2 -2
View File
@@ -43,7 +43,7 @@ jobs:
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
@@ -82,7 +82,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+2 -2
View File
@@ -40,7 +40,7 @@ jobs:
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
@@ -81,7 +81,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+2 -2
View File
@@ -31,7 +31,7 @@ jobs:
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
@@ -67,7 +67,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+30 -30
View File
@@ -1,35 +1,35 @@
FROM alpine:3.19
FROM alpine:3.23
# Apache + PHP
RUN apk add --no-cache \
apache2 \
php82 \
php82-common \
php82-apache2 \
php82-curl \
php82-ldap \
php82-mysqli \
php82-gd \
php82-xml \
php82-mbstring \
php82-zip \
php82-ctype \
php82-tokenizer \
php82-pdo_mysql \
php82-openssl \
php82-bcmath \
php82-phar \
php82-json \
php82-iconv \
php82-fileinfo \
php82-simplexml \
php82-session \
php82-dom \
php82-xmlwriter \
php82-xmlreader \
php82-sodium \
php82-redis \
php82-pecl-memcached \
php82-exif \
php84 \
php84-common \
php84-apache2 \
php84-curl \
php84-ldap \
php84-mysqli \
php84-gd \
php84-xml \
php84-mbstring \
php84-zip \
php84-ctype \
php84-tokenizer \
php84-pdo_mysql \
php84-openssl \
php84-bcmath \
php84-phar \
php84-json \
php84-iconv \
php84-fileinfo \
php84-simplexml \
php84-session \
php84-dom \
php84-xmlwriter \
php84-xmlreader \
php84-sodium \
php84-redis \
php84-pecl-memcached \
php84-exif \
curl \
wget \
vim \
@@ -42,7 +42,7 @@ COPY docker/column-statistics.cnf /etc/mysql/conf.d/column-statistics.cnf
# Where apache's PID lives
RUN mkdir -p /run/apache2 && chown apache:apache /run/apache2
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php82/php.ini
RUN sed -i 's/variables_order = .*/variables_order = "EGPCS"/' /etc/php84/php.ini
COPY docker/000-default-2.4.conf /etc/apache2/conf.d/default.conf
# Enable mod_rewrite
+2
View File
@@ -78,6 +78,8 @@ Since the release of the JSON REST API, several third-party developers have been
#### Libraries & Modules
- [SnipeScheduler](https://github.com/JSY-Ben/SnipeScheduler) by [@JSY-Ben](https://github.com/JSY-Ben) - An Asset Reservation/Checkout System for Snipe-IT
- [Snipe-IT MCP Server](https://github.com/jameshgordy/snipeit-mcp) by [@jameshgordy](https://github.com/jameshgordy) - A Model Context Protocol (MCP) server for managing Snipe-IT inventory systems
- [SnipeSharp - .NET module in C#](https://github.com/barrycarey/SnipeSharp) by [@barrycarey](https://github.com/barrycarey)
- [SnipeitPS](https://github.com/snazy2000/SnipeitPS) by [@snazy2000](https://github.com/snazy2000) - Powershell API Wrapper for Snipe-it
- [jamf2snipe](https://github.com/grokability/jamf2snipe) - Python script to sync assets between a JAMFPro instance and a Snipe-IT instance
+21 -20
View File
@@ -245,26 +245,26 @@ class LdapSync extends Command
// Assign the mapped LDAP attributes for each user to the Snipe-IT user fields
for ($i = 0; $i < $results['count']; $i++) {
$item = [];
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? '';
$item['display_name'] = $results[$i][$ldap_map["display_name"]][0] ?? '';
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? '';
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? '';
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? '';
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? '';
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? '';
$item['location_id'] = $results[$i]['location_id'] ?? '';
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? '';
$item['mobile'] = $results[$i][$ldap_map["mobile"]][0] ?? '';
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? '';
$item['address'] = $results[$i][$ldap_map["address"]][0] ?? '';
$item['city'] = $results[$i][$ldap_map["city"]][0] ?? '';
$item['state'] = $results[$i][$ldap_map["state"]][0] ?? '';
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? '';
$item['zip'] = $results[$i][$ldap_map["zip"]][0] ?? '';
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? '';
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? '';
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? '';
$location = $default_location; //initially, set '$location' to the default_location (which may just be `null`)
$item['username'] = $results[$i][$ldap_map["username"]][0] ?? null;
$item['display_name'] = $results[$i][$ldap_map["display_name"]][0] ?? null;
$item['employee_number'] = $results[$i][$ldap_map["emp_num"]][0] ?? null;
$item['lastname'] = $results[$i][$ldap_map["last_name"]][0] ?? null;
$item['firstname'] = $results[$i][$ldap_map["first_name"]][0] ?? null;
$item['email'] = $results[$i][$ldap_map["email"]][0] ?? null;
$item['ldap_location_override'] = $results[$i]['ldap_location_override'] ?? null;
$item['location_id'] = $results[$i]['location_id'] ?? null;
$item['telephone'] = $results[$i][$ldap_map["phone"]][0] ?? null;
$item['mobile'] = $results[$i][$ldap_map["mobile"]][0] ?? null;
$item['jobtitle'] = $results[$i][$ldap_map["jobtitle"]][0] ?? null;
$item['address'] = $results[$i][$ldap_map["address"]][0] ?? null;
$item['city'] = $results[$i][$ldap_map["city"]][0] ?? null;
$item['state'] = $results[$i][$ldap_map["state"]][0] ?? null;
$item['country'] = $results[$i][$ldap_map["country"]][0] ?? null;
$item['zip'] = $results[$i][$ldap_map["zip"]][0] ?? null;
$item['department'] = $results[$i][$ldap_map["dept"]][0] ?? null;
$item['manager'] = $results[$i][$ldap_map["manager"]][0] ?? null;
$item['location'] = $results[$i][$ldap_map["location"]][0] ?? null;
$location = $default_location; //initially, set '$location' to the default_location (which may just be null)
// ONLY if you are using the "ldap_location" option *AND* you have an actual result
if ($ldap_map["location"] && $item['location']) {
@@ -464,6 +464,7 @@ class LdapSync extends Command
$errors = '';
if ($user->save()) {
$item['id'] = $user->id;
$item['note'] = $item['createorupdate'];
$item['status'] = 'success';
if ($item['createorupdate'] === 'created' && $ldap_default_group) {
@@ -0,0 +1,74 @@
<?php
namespace App\Console\Commands;
use App\Enums\ActionType;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class MigrateLicenseSeatQuantitiesInActionLogs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:migrate-license-seat-quantities-in-action-logs
{--no-interaction: Do not ask any interactive question}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Updates quantity field in action_logs table for license seats that were added or deleted.';
/**
* Execute the console command.
*/
public function handle()
{
$query = DB::table('action_logs')
->whereIn('action_type', [
ActionType::AddSeats->value,
ActionType::DeleteSeats->value,
])
->where('quantity', '=', 1)
->orderBy('id');
$count = $query->count();
if ($count === 0) {
$this->info('Nothing to update');
return 0;
}
$this->info("{$count} logs to update");
if ($this->option('no-interaction') || $this->confirm('Update quantities in the action log?')) {
$query->chunk(50, function ($logs) {
$logs->each(function ($log) {
$quantityFromNote = Str::between($log->note, "ed ", " seats");
if (!is_numeric($quantityFromNote)) {
$this->error('Could not parse quantity from ID: {id}', ['id' => $log->id]);
}
if ($log->quantity !== (int) $quantityFromNote) {
$this->info(vsprintf('Updating id: %s to quantity %s', [
'id' => $log->id,
'new_quantity' => $quantityFromNote,
]));
DB::table('action_logs')->where('id', $log->id)->update(['quantity' => (int) $quantityFromNote]);
}
});
});
}
return 0;
}
}
+12 -5
View File
@@ -33,6 +33,11 @@ class ObjectImportCommand extends Command
*/
protected ProgressIndicator $progressIndicator;
/**
* Logger instance with configurable log path
*/
protected $logger;
/**
* Create a new command instance.
*
@@ -65,9 +70,11 @@ class ObjectImportCommand extends Command
->setShouldNotify($this->option('send-welcome'))
->setUsernameFormat($this->option('username_format'));
// This $logFile/useFiles() bit is currently broken, so commenting it out for now
// $logFile = $this->option('logfile');
// Log::useFiles($logFile);
$this->logger = Log::build([
'driver' => 'single',
'path' => $this->option('logfile'),
]);
$this->progressIndicator->start('======= Importing Items from '.$filename.' =========');
$importer->import();
@@ -99,10 +106,10 @@ class ObjectImportCommand extends Command
public function log($string, $level = 'info')
{
if ($level === 'warning') {
Log::warning($string);
$this->logger->warning($string);
$this->comment($string);
} else {
Log::Info($string);
$this->logger->Info($string);
if ($this->option('verbose')) {
$this->comment($string);
}
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace App\Console\Commands;
use App\Models\CheckoutAcceptance;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class PurgeEulaPDFs extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:purge-eula-pdfs
{--older-than-days= : The number of days we should delete before }
{--force : Skip the interactive yes/no prompt for confirmation}
{--dryrun : Show the records that would be deleted but don\'t update the database or delete files from disk}
{--with-output : Display the results in a table in your console}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This purges signature files and EULAs from the system if they are older than the date passed with --older-than-days=.';
/**
* Execute the console command.
*/
public function handle()
{
$before = $this->option('older-than-days');
if (($before=='') || (!is_numeric($before))) {
return $this->error('ERROR: You must pass a valid number for --older-than-days (example: snipeit:purge-eula-pdfs --older-than-days=365.)');
}
$interval_date = Carbon::now()->subDays($before);
$signature_path = 'private_uploads/signatures/';
$eula_path = 'private_uploads/eula-pdfs/';
if (!Storage::exists($eula_path)) {
$this->fail('The storage directory "'.$eula_path.'" does not exist. No EULA files will be deleted.');
}
if (!Storage::exists($signature_path)) {
$this->fail('The storage directory "'.$signature_path.'" does not exist. No signature files will be deleted.');
}
if ($this->option('dryrun')) {
$this->info('This script is being run with the --dryrun option. No files or records will be deleted.');
}
$acceptances = CheckoutAcceptance::HasFiles()->where('updated_at','<', $interval_date)->with('assignedTo')->get();
if (!$this->option('force')) {
if ($this->confirm("\n****************************************************\nTHIS WILL DELETE ALL OF THE SIGNATURES AND EULA PDF FILES SINCE $interval_date. \nThere is NO undo! \n****************************************************\n\nDo you wish to continue? No backsies! [y|N]")) {
}
}
if ($acceptances->count() == 0) {
return $this->warn('There are no item acceptances with signatures or EULA PDFs from before '.$interval_date);
}
$this->info(number_format($acceptances->count()) . ' EULA PDFs from before '.$interval_date.' will be purged');
if (!$this->option('with-output')) {
$this->info('Run this command with the --with-output option to see the full list in the console.');
} else {
$this->table(
[
trans('general.user'),
trans('general.type'),
trans('general.item'),
trans('general.category'),
trans('general.accepted_date'),
trans('general.declined_date'),
trans('general.signature'),
trans('general.filename'),
],
$acceptances->map(fn($acceptance) => [
trans('general.user') => $acceptance->assignedTo->display_name,
trans('general.type') => $acceptance->display_checkoutable_type,
trans('general.item') => $acceptance->checkoutable_type::find($acceptance->checkoutable_id)->display_name,
trans('general.category') => $acceptance->checkoutable_category_name,
trans('general.accepted_date') => $acceptance->accepted_at,
trans('general.declined_date') => $acceptance->declined_at,
trans('general.signature') => $acceptance->signature_filename,
trans('general.filename') => $acceptance->stored_eula_file,
])
);
}
foreach ($acceptances as $acceptance) {
$signature_file = $signature_path.$acceptance->signature_filename;
$eula_file = $eula_path.$acceptance->stored_eula_file;
if (Storage::exists($signature_file)) {
if (!$this->option('dryrun')) {
Storage::delete($signature_file);
}
} else {
$this->error('The file "'. $signature_file.'" does not exist.');
}
if (Storage::exists($eula_file)) {
if (!$this->option('dryrun')) {
Storage::delete($eula_file);
}
} else {
$this->error('The file "'.$eula_file.'" does not exist.');
}
if (!$this->option('dryrun')) {
$acceptance->delete();
}
}
}
}
+2 -2
View File
@@ -50,8 +50,8 @@ class ResetDemoSettings extends Command
$settings->alert_email = 'service@snipe-it.io';
$settings->login_note = 'Use `admin` / `password` to login to the demo.';
$settings->header_color = '#3c8dbc';
$settings->link_dark_color = '#86cbf2';
$settings->link_light_color = '#084d73;';
$settings->link_dark_color = '#5fa4cc';
$settings->link_light_color = '#296282;';
$settings->label2_2d_type = 'QRCODE';
$settings->default_currency = 'USD';
$settings->brand = 2;
@@ -55,6 +55,8 @@ class SendExpirationAlerts extends Command
// Expiring Assets
$assets = Asset::getExpiringWarrantyOrEol($alert_interval);
$assets->load(['assignedTo', 'supplier']);
if ($assets->count() > 0) {
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
+3 -1
View File
@@ -15,18 +15,20 @@ class CheckoutableCheckedOut
public $checkedOutBy;
public $note;
public $originalValues;
public int $quantity;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note, $originalValues = [])
public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note, $originalValues = [], $quantity = 1)
{
$this->checkoutable = $checkoutable;
$this->checkedOutTo = $checkedOutTo;
$this->checkedOutBy = $checkedOutBy;
$this->note = $note;
$this->originalValues = $originalValues;
$this->quantity = $quantity;
}
}
+97 -5
View File
@@ -2,6 +2,7 @@
namespace App\Helpers;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Component;
@@ -13,6 +14,7 @@ use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\License;
use App\Models\Location;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
@@ -776,7 +778,7 @@ class Helper
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
$components = Component::whereNotNull('min_amt')->get();
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
$asset_models = AssetModel::where('min_amt', '>', 0)->withCount(['availableAssets', 'assets'])->get();
$licenses = License::where('min_amt', '>', 0)->get();
$items_array = [];
@@ -842,8 +844,8 @@ class Helper
foreach ($asset_models as $asset_model){
$asset = new Asset();
$total_owned = $asset->where('model_id', '=', $asset_model->id)->count();
$avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count();
$total_owned = $asset_model->assets_count; //requires the withCount() clause in the initial query!
$avail = $asset_model->available_assets_count; //requires the withCount() clause in the initial query!
if ($avail <= ($asset_model->min_amt) + $alert_threshold) {
if ($avail > 0) {
@@ -1384,7 +1386,19 @@ class Helper
* @return string[]
*/
public static function SettingUrls(){
$settings=['#','fields.index', 'statuslabels.index', 'models.index', 'categories.index', 'manufacturers.index', 'suppliers.index', 'departments.index', 'locations.index', 'companies.index', 'depreciations.index'];
$settings=[
'#',
'fields*',
'statuslabels*',
'models*',
'categories*',
'manufacturers*',
'suppliers*',
'departments*',
'locations*',
'companies*',
'depreciations*'
];
return $settings;
}
@@ -1570,7 +1584,6 @@ class Helper
]) ? 'rtl' : 'ltr';
}
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
{
@@ -1735,4 +1748,83 @@ class Helper
}
return $mismatched;
}
static public function labelFieldLayoutScaling(
$pdf,
iterable|\Closure $fields,
float $currentX,
float $usableWidth,
float $usableHeight,
float $baseLabelSize,
float $baseFieldSize,
float $baseFieldMargin,
?string $title = null,
float $baseTitleSize = 0.0,
float $baseTitleMargin = 0.0,
float $baseLabelPadding = 1.5,
float $baseGap = 1.5,
float $maxScale = 1.8,
string $labelFont = 'freesans',
) : array
{
$fieldCount = count($fields);
$perFieldHeight = max($baseLabelSize, $baseFieldSize) + $baseFieldMargin;
$baseFieldsHeight = $fieldCount * $perFieldHeight;
$hasTitle = is_string($title) && trim($title) !== '';
$baseTitleHeight = $hasTitle ? ($baseTitleSize + $baseTitleMargin) : 0.0;
$baseTotalHeight = $baseTitleHeight + $baseFieldsHeight;
$scale = 1.0;
if ($baseTotalHeight > 0 && $usableHeight > 0) {
$scale = $usableHeight / $baseTotalHeight;
}
$scale = min($scale, $maxScale);
$labelSize = $baseLabelSize;
$fieldSize = $baseFieldSize * $scale;
$fieldMargin = $baseFieldMargin * $scale;
$rowAdvance = max($labelSize, $fieldSize) + $fieldMargin;
$titleSize = $hasTitle ? ($baseTitleSize * $scale) : 0.0;
$titleMargin = $hasTitle ? ($baseTitleMargin * $scale) : 0.0;
$titleAdvance = $hasTitle ? ($titleSize + $titleMargin) : 0.0;
$pdf->SetFont($labelFont, '', $baseLabelSize);
$maxLabelWidthPerUnit = 0;
foreach ($fields as $field) {
$rawLabel = $field['label'] ?? null;
// If no label, do not include it in label-column sizing
if (!is_string($rawLabel) || trim($rawLabel) === '') {
continue;
}
$label = rtrim($field['label'], ':') . ':';
$width = $pdf->GetStringWidth($label);
$maxLabelWidthPerUnit = max($maxLabelWidthPerUnit, $width / $baseLabelSize);
}
$labelPadding = $baseLabelPadding * $scale;
$gap = $baseGap * $scale;
$labelWidth = ($maxLabelWidthPerUnit * $labelSize) + $labelPadding;
$valueX = $currentX + $labelWidth + $gap;
$valueWidth = $usableWidth - $labelWidth - $gap;
return compact(
'scale',
'hasTitle',
'titleSize',
'titleMargin',
'titleAdvance',
'labelSize',
'fieldSize',
'fieldMargin',
'rowAdvance',
'labelWidth',
'valueX',
'valueWidth'
);
}
}
+55 -2
View File
@@ -46,6 +46,8 @@ class IconHelper
return 'fa-regular fa-envelope';
case 'phone':
return 'fa-solid fa-phone';
case 'fax':
return 'fa-solid fa-fax';
case 'mobile':
return 'fas fa-mobile-screen-button';
case 'long-arrow-right':
@@ -53,7 +55,7 @@ class IconHelper
case 'download':
return 'fas fa-download';
case 'checkmark':
return 'fas fa-check icon-white';
return 'fas fa-check';
case 'x':
return 'fas fa-times';
case 'logout':
@@ -129,9 +131,12 @@ class IconHelper
return 'fa-regular fa-clipboard';
case 'paperclip':
return 'fas fa-paperclip';
case 'contact-card':
return 'fa-regular fa-id-card';
case 'files':
return 'fa-regular fa-file';
case 'more-info':
case 'support':
return 'far fa-life-ring';
case 'calendar':
return 'fas fa-calendar';
@@ -142,7 +147,7 @@ class IconHelper
case 'more-files':
return 'fa-solid fa-laptop-file';
case 'maintenances':
return 'fas fa-wrench';
return 'fa-solid fa-screwdriver-wrench';
case 'seats':
return 'far fa-list-alt';
case 'globe-us':
@@ -201,6 +206,54 @@ class IconHelper
return 'fa-solid fa-lightbulb';
case 'highlight':
return 'fa-solid fa-highlighter';
case 'manager':
return 'fa-solid fa-building-user';
case 'company':
return 'fa-regular fa-building';
case 'parent':
return 'fa-solid fa-building-flag';
case 'number':
return 'fa-solid fa-hashtag';
case 'depreciation':
return 'fa-solid fa-arrows-down-to-line';
case 'depreciation-calendar':
case 'expiration':
case 'terminates':
return 'fa-regular fa-calendar-xmark';
case 'manufacturer':
return 'fa-solid fa-industry';
case 'fieldset' :
return 'fa-regular fa-rectangle-list';
case 'deleted-date':
return 'fa-solid fa-calendar-xmark';
case 'eol':
return 'fa-regular fa-calendar-days';
case 'category':
return 'fa-solid fa-icons';
case 'cost':
return 'fa-solid fa-money-bills';
case 'available':
return 'fa-solid fa-box';
case 'checkedout':
return 'fa-solid fa-box-open';
case 'purchase_order':
return 'fa-solid fa-file-invoice-dollar';
case 'order':
return 'fa-solid fa-file-invoice';
case 'checkout-all':
return 'fa-solid fa-arrows-down-to-people';
case 'square-right':
return 'fa-regular fa-square-caret-right';
case 'square-left':
return 'fa-regular fa-square-caret-left';
case 'square':
return 'fa-solid fa-square';
case 'models':
case 'model':
return 'fa-solid fa-boxes-stacked';
case 'min-qty':
return 'fa-solid fa-chart-pie';
}
}
}
@@ -90,10 +90,10 @@ class AccessoriesController extends Controller
$accessory = $request->handleImages($accessory);
}
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
// Was the accessory created?
@@ -182,10 +182,10 @@ class AccessoriesController extends Controller
$accessory = $request->handleImages($accessory);
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
if ($accessory->save()) {
@@ -76,7 +76,7 @@ class AccessoryCheckinController extends Controller
if ($accessory_checkout->delete()) {
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkin.success'));
@@ -67,7 +67,7 @@ class AccessoryCheckoutController extends Controller
*/
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
{
$this->authorize('checkout', $accessory);
$target = $this->determineCheckoutTarget();
@@ -89,12 +89,19 @@ class AccessoryCheckoutController extends Controller
$accessory_checkout->save();
}
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
event(new CheckoutableCheckedOut(
$accessory,
$target,
auth()->user(),
$request->input('note'),
[],
$accessory->checkout_qty,
));
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
$request->request->add(['assigned_to' => $target->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => $request->input('checkout_to_type')]);
// Redirect to the new accessory page
@@ -325,7 +325,14 @@ class AccessoriesController extends Controller
}
// Set this value to be able to pass the qty through to the event
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
event(new CheckoutableCheckedOut(
$accessory,
$target,
auth()->user(),
$request->input('note'),
[],
$accessory->checkout_qty,
));
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/accessories/message.checkout.success')));
@@ -389,7 +396,7 @@ class AccessoriesController extends Controller
]);
if ($request->filled('search')) {
$accessories = $accessories->where('accessories.name', 'LIKE', '%'.$request->get('search').'%');
$accessories = $accessories->where('accessories.name', 'LIKE', '%'.$request->input('search').'%');
}
$accessories = $accessories->orderBy('name', 'ASC')->paginate(50);
@@ -249,7 +249,7 @@ class AssetModelsController extends Controller
* it, but I'll be damned if I can think of one. - snipe
*/
if ($request->filled('custom_fieldset_id')) {
$assetmodel->fieldset_id = $request->get('custom_fieldset_id');
$assetmodel->fieldset_id = $request->input('custom_fieldset_id');
}
+29 -16
View File
@@ -15,6 +15,7 @@ use App\Http\Transformers\ComponentsTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\AccessoryCheckout;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\CheckoutAcceptance;
@@ -153,6 +154,15 @@ class AssetsController extends Controller
}
$assets = Asset::select('assets.*')
->addSelect([
'first_checkout_at' => Actionlog::query()
->select('created_at')
->whereColumn('item_id', 'assets.id')
->where('item_type', Asset::class)
->where('action_type', 'checkout')
->orderBy('created_at')
->limit(1),
])
->with(
'model',
'location',
@@ -377,7 +387,7 @@ class AssetsController extends Controller
}
if ($request->filled('order_number')) {
$assets->where('assets.order_number', '=', strval($request->get('order_number')));
$assets->where('assets.order_number', '=', strval($request->input('order_number')));
}
// This is kinda gross, but we need to do this because the Bootstrap Tables
@@ -654,7 +664,7 @@ class AssetsController extends Controller
public function store(StoreAssetRequest $request): JsonResponse
{
$asset = new Asset();
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
$asset->model()->associate(AssetModel::find((int) $request->input('model_id')));
$asset->fill($request->validated());
$asset->created_by = auth()->id();
@@ -683,8 +693,8 @@ class AssetsController extends Controller
// If input value is null, use custom field's default value
if ($field_val == null) {
Log::debug('Field value for ' . $field->db_column . ' is null');
$field_val = $field->defaultValue($request->get('model_id'));
Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id')));
$field_val = $field->defaultValue($request->input('model_id'));
Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->input('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
@@ -695,7 +705,7 @@ class AssetsController extends Controller
// If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id') != '')) {
$field_val = Crypt::encrypt($field->defaultValue($request->get('model_id')));
$field_val = Crypt::encrypt($field->defaultValue($request->input('model_id')));
} else {
$field_val = Crypt::encrypt($request->input($field->db_column));
}
@@ -713,15 +723,15 @@ class AssetsController extends Controller
}
if ($asset->save()) {
if ($request->get('assigned_user')) {
if ($request->input('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif ($request->get('assigned_asset')) {
} elseif ($request->input('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
} elseif ($request->get('assigned_location')) {
} elseif ($request->input('assigned_location')) {
$target = Location::find(request('assigned_location'));
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')));
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->input('name')));
}
if ($asset->image) {
@@ -798,19 +808,22 @@ class AssetsController extends Controller
}
}
if ($asset->save()) {
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
if (($request->filled('assigned_user')) && ($target = User::find($request->input('assigned_user')))) {
$location = $target->location_id;
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->input('assigned_asset')))) {
$location = $target->location_id;
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
->update(['location_id' => $target->location_id]);
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->input('assigned_location')))) {
$location = $target->id;
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
// Using `->has` preserves the asset name if the name parameter was not included in request.
$asset_name = request()->has('name') ? request('name') : $asset->name;
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', $asset_name, $location);
}
if ($asset->image) {
@@ -954,7 +967,7 @@ class AssetsController extends Controller
}
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
$asset->status_id = $request->input('status_id');
}
if (! isset($target)) {
@@ -1034,7 +1047,7 @@ class AssetsController extends Controller
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at') . ' ' . date('H:i:s') : date('Y-m-d H:i:s');
$originalValues = $asset->getRawOriginal();
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
if (($request->filled('checkin_at')) && ($request->input('checkin_at') != date('Y-m-d'))) {
$originalValues['action_date'] = $checkin_at;
}
@@ -1143,7 +1156,7 @@ class AssetsController extends Controller
/**
* Update custom fields in the database.
* Validation for these fields is handled through the AssetRequest form request
* $model = AssetModel::find($request->get('model_id'));
* $model = AssetModel::find($request->input('model_id'));
*/
if (($asset->model) && ($asset->model->fieldset)) {
$payload['custom_fields'] = [];
@@ -265,7 +265,7 @@ class CategoriesController extends Controller
]);
if ($request->filled('search')) {
$categories = $categories->where('name', 'LIKE', '%'.$request->get('search').'%');
$categories = $categories->where('name', 'LIKE', '%'.$request->input('search').'%');
}
$categories = $categories->where('category_type', $category_type)->orderBy('name', 'ASC')->paginate(50);
@@ -202,7 +202,7 @@ class CompaniesController extends Controller
if ($request->filled('search')) {
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->get('search').'%');
$companies = $companies->where('companies.name', 'LIKE', '%'.$request->input('search').'%');
}
$companies = $companies->orderBy('name', 'ASC')->paginate(50);
@@ -59,7 +59,7 @@ class ComponentsController extends Controller
$components = Component::select('components.*')
->with('company', 'location', 'category', 'supplier', 'adminuser', 'manufacturer')
->withSum('uncontrainedAssets as sum_unconstrained_assets', 'components_assets.assigned_qty');
->withSum('unconstrainedAssets as sum_unconstrained_assets', 'components_assets.assigned_qty');
$filter = [];
@@ -303,11 +303,11 @@ class ComponentsController extends Controller
}
// Make sure there is at least one available to checkout
if ($component->numRemaining() < $request->get('assigned_qty')) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
if ($component->numRemaining() < $request->input('assigned_qty')) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->input('assigned_qty')])));
}
if ($component->numRemaining() >= $request->get('assigned_qty')) {
if ($component->numRemaining() >= $request->input('assigned_qty')) {
$asset = Asset::find($request->input('assigned_to'));
$component->assigned_to = $request->input('assigned_to');
@@ -315,18 +315,18 @@ class ComponentsController extends Controller
$component->assets()->attach($component->id, [
'component_id' => $component->id,
'created_at' => Carbon::now(),
'assigned_qty' => $request->get('assigned_qty', 1),
'assigned_qty' => $request->input('assigned_qty', 1),
'created_by' => auth()->id(),
'asset_id' => $request->get('assigned_to'),
'note' => $request->get('note'),
'asset_id' => $request->input('assigned_to'),
'note' => $request->input('note'),
]);
$component->logCheckout($request->input('note'), $asset);
$component->logCheckout($request->input('note'), $asset, null, [], $request->get('assigned_qty', 1));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.checkout.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->get('assigned_qty')])));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/components/message.checkout.unavailable', ['remaining' => $component->numRemaining(), 'requested' => $request->input('assigned_qty')])));
}
/**
@@ -326,8 +326,14 @@ class ConsumablesController extends Controller
);
}
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
event(new CheckoutableCheckedOut(
$consumable,
$user,
auth()->user(),
$request->input('note'),
[],
$consumable->checkout_qty,
));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/consumables/message.checkout.success')));
@@ -346,7 +352,7 @@ class ConsumablesController extends Controller
]);
if ($request->filled('search')) {
$consumables = $consumables->where('consumables.name', 'LIKE', '%'.$request->get('search').'%');
$consumables = $consumables->where('consumables.name', 'LIKE', '%'.$request->input('search').'%');
}
$consumables = $consumables->orderBy('name', 'ASC')->paginate(50);
@@ -195,7 +195,7 @@ class DepartmentsController extends Controller
]);
if ($request->filled('search')) {
$departments = $departments->where('name', 'LIKE', '%'.$request->get('search').'%');
$departments = $departments->where('name', 'LIKE', '%'.$request->input('search').'%');
}
$departments = $departments->orderBy('name', 'ASC')->paginate(50);
@@ -196,7 +196,7 @@ class ImportController extends Controller
$this->authorize('import');
// Run a backup immediately before processing
if ($request->get('run-backup')) {
if ($request->input('run-backup')) {
Log::debug('Backup manually requested via importer');
Artisan::call('snipeit:backup', ['--filename' => 'pre-import-backup-'.date('Y-m-d-H-i-s')]);
} else {
@@ -212,7 +212,7 @@ class ImportController extends Controller
$errors = $request->import($import);
$redirectTo = 'hardware.index';
switch ($request->get('import-type')) {
switch ($request->input('import-type')) {
case 'asset':
$model_perms = 'App\Models\Asset';
$redirectTo = 'hardware.index';
@@ -24,7 +24,7 @@ class LabelsController extends Controller
$labels = Label::find();
if ($request->filled('search')) {
$search = $request->get('search');
$search = $request->input('search');
$labels = $labels->filter(function ($label, $index) use ($search) {
return stripos($label->getName(), $search) !== false;
});
@@ -32,11 +32,11 @@ class LabelsController extends Controller
$total = $labels->count();
$offset = $request->get('offset', 0);
$offset = $request->input('offset', 0);
$offset = ($offset > $total) ? $total : $offset;
$maxLimit = config('app.max_results');
$limit = $request->get('limit', $maxLimit);
$limit = $request->input('limit', $maxLimit);
$limit = ($limit > $maxLimit) ? $maxLimit : $limit;
$labels = $labels->skip($offset)->take($limit);
@@ -265,7 +265,7 @@ class LicensesController extends Controller
]);
if ($request->filled('search')) {
$licenses = $licenses->where('licenses.name', 'LIKE', '%'.$request->get('search').'%');
$licenses = $licenses->where('licenses.name', 'LIKE', '%'.$request->input('search').'%');
}
$licenses = $licenses->orderBy('name', 'ASC')->paginate(50);
@@ -200,7 +200,7 @@ class LocationsController extends Controller
// Only scope location if the setting is enabled
if (Setting::getSettings()->scope_locations_fmcs) {
$location->company_id = Company::getIdForCurrentUser($request->get('company_id'));
$location->company_id = Company::getIdForCurrentUser($request->input('company_id'));
// check if parent is set and has a different company
if ($location->parent_id && Location::find($location->parent_id)->company_id != $location->company_id) {
response()->json(Helper::formatStandardApiResponse('error', null, 'different company than parent'));
@@ -278,13 +278,13 @@ class LocationsController extends Controller
if ($request->filled('company_id')) {
// Only scope location if the setting is enabled
if (Setting::getSettings()->scope_locations_fmcs) {
$location->company_id = Company::getIdForCurrentUser($request->get('company_id'));
$location->company_id = Company::getIdForCurrentUser($request->input('company_id'));
// check if there are related objects with different company
if (Helper::test_locations_fmcs(false, $id, $location->company_id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, 'error scoped locations'));
}
} else {
$location->company_id = $request->get('company_id');
$location->company_id = $request->input('company_id');
}
}
@@ -274,7 +274,7 @@ class ManufacturersController extends Controller
]);
if ($request->filled('search')) {
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->get('search').'%');
$manufacturers = $manufacturers->where('name', 'LIKE', '%'.$request->input('search').'%');
}
$manufacturers = $manufacturers->orderBy('name', 'ASC')->paginate(50);
@@ -145,7 +145,7 @@ class PredefinedKitsController extends Controller
]);
if ($request->filled('search')) {
$kits = $kits->where('name', 'LIKE', '%'.$request->get('search').'%');
$kits = $kits->where('name', 'LIKE', '%'.$request->input('search').'%');
}
$kits = $kits->orderBy('name', 'ASC')->paginate(50);
@@ -184,7 +184,7 @@ class PredefinedKitsController extends Controller
$quantity = 1;
}
$license_id = $request->get('license');
$license_id = $request->input('license');
$relation = $kit->licenses();
if ($relation->find($license_id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, ['license' => trans('admin/kits/general.license_error')]));
@@ -254,7 +254,7 @@ class PredefinedKitsController extends Controller
$kit = PredefinedKit::findOrFail($kit_id);
$model_id = $request->get('model');
$model_id = $request->input('model');
$quantity = $request->input('quantity', 1);
if ($quantity < 1) {
$quantity = 1;
@@ -332,7 +332,7 @@ class PredefinedKitsController extends Controller
$quantity = 1;
}
$consumable_id = $request->get('consumable');
$consumable_id = $request->input('consumable');
$relation = $kit->consumables();
if ($relation->find($consumable_id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, ['consumable' => trans('admin/kits/general.consumable_error')]));
@@ -406,7 +406,7 @@ class PredefinedKitsController extends Controller
$quantity = 1;
}
$accessory_id = $request->get('accessory');
$accessory_id = $request->input('accessory');
$relation = $kit->accessories();
if ($relation->find($accessory_id)) {
return response()->json(Helper::formatStandardApiResponse('error', null, ['accessory' => trans('admin/kits/general.accessory_error')]));
+14 -10
View File
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\ProfileTransformer;
use App\Models\CheckoutRequest;
use App\Models\Setting;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
@@ -182,19 +183,22 @@ class ProfileController extends Controller
*/
public function eulas(ProfileTransformer $transformer, Request $request)
{
if($request->filled('user_id') && $request->input('user_id') != 0) {
// Return selected user's EULAs
$eulas = User::find($request->input('user_id'))->eulas;
}
else {
// Only return this user's EULAs
if (($request->filled('user_id')) && ($request->input( 'user_id') != 0)) {
$eula_user = User::find($request->input('user_id'));
if (($eula_user) && (Setting::getSettings()->manager_view_enabled) && (auth()->user()->isManagerOf($eula_user))) {
$eulas = $eula_user->eulas;
} else {
return response()->json(Helper:: formatStandardApiResponse('error', null, trans('admin/users/message.user_not_found')));
}
} else {
$eulas = auth()->user()->eulas;
}
return response()->json(
$transformer->transformFiles($eulas, $eulas->count())
);
return response()->json($transformer->transformFiles($eulas, $eulas->count()));
}
}
@@ -226,7 +226,7 @@ class SettingsController extends Controller
$login_attempts = DB::table('login_attempts');
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'created_at';
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$total = $login_attempts->count();
$login_attempts->orderBy($sort, $order);
@@ -324,7 +324,7 @@ class StatuslabelsController extends Controller
$statuslabels = Statuslabel::orderBy('default_label', 'desc')->orderBy('name', 'asc')->orderBy('deployable', 'desc');
if ($request->filled('search')) {
$statuslabels = $statuslabels->where('name', 'LIKE', '%'.$request->get('search').'%');
$statuslabels = $statuslabels->where('name', 'LIKE', '%'.$request->input('search').'%');
}
if ($request->filled('deployable')) {
@@ -256,7 +256,7 @@ class SuppliersController extends Controller
]);
if ($request->filled('search')) {
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->get('search').'%');
$suppliers = $suppliers->where('suppliers.name', 'LIKE', '%'.$request->input('search').'%');
}
$suppliers = $suppliers->orderBy('name', 'ASC')->paginate(50);
@@ -110,15 +110,18 @@ class UploadedFilesController extends Controller
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
$files[] = $file_name;
$object->logUpload($file_name, $request->get('notes'));
$object->logUpload($file_name, $request->input('notes'));
}
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
->where('item_type', '=', self::$map_object_type[$object_type])
->where('item_id', '=', $id)->whereIn('filename', $files)
->get();
if (isset($files)) {
$file_results = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
->where('item_type', '=', self::$map_object_type[$object_type])
->where('item_id', '=', $id)->whereIn('filename', $files)
->get();
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($file_results, count($file_results)), trans_choice('general.file_upload_status.upload.success', count($files))));
}
return response()->json(Helper::formatStandardApiResponse('success', (new UploadedFilesTransformer())->transformFiles($files, count($files)), trans_choice('general.file_upload_status.upload.success', count($files))));
}
// No files were submitted
+37 -21
View File
@@ -253,7 +253,7 @@ class UsersController extends Controller
}
if ($request->filled('group_id')) {
$users = $users->ByGroup($request->get('group_id'));
$users = $users->ByGroup($request->input('group_id'));
}
if ($request->filled('department_id')) {
@@ -400,11 +400,11 @@ class UsersController extends Controller
if ($request->filled('search')) {
$users = $users->where(function ($query) use ($request) {
$query->SimpleNameSearch($request->get('search'))
->orWhere('username', 'LIKE', '%'.$request->get('search').'%')
->orWhere('display_name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('email', 'LIKE', '%'.$request->get('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->get('search').'%');
$query->SimpleNameSearch($request->input('search'))
->orWhere('username', 'LIKE', '%'.$request->input('search').'%')
->orWhere('display_name', 'LIKE', '%'.$request->input('search').'%')
->orWhere('email', 'LIKE', '%'.$request->input('search').'%')
->orWhere('employee_num', 'LIKE', '%'.$request->input('search').'%');
});
}
@@ -459,7 +459,7 @@ class UsersController extends Controller
//
if ($request->filled('password')) {
$user->password = bcrypt($request->get('password'));
$user->password = bcrypt($request->input('password'));
} else {
$user->password = $user->noPassword();
}
@@ -478,12 +478,22 @@ class UsersController extends Controller
}
if ($request->filled('groups')) {
if (($request->has('groups')) && (auth()->user()->isSuperUser())) {
$validator = Validator::make($request->only('groups'), [
'groups.*' => 'integer|exists:permission_groups,id',
]);
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()));
}
// Sync the groups since the user is a superuser and the groups pass validation
$user->groups()->sync($request->input('groups'));
} else {
$user->groups()->sync([]);
}
return response()->json(Helper::formatStandardApiResponse('success', (new UsersTransformer)->transformUser($user), trans('admin/users/message.success.create')));
}
@@ -632,21 +642,27 @@ class UsersController extends Controller
$this->authorize('delete', $user);
if ($user->delete()) {
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
// Remove the user's avatar if they have one
if (Storage::disk('public')->exists('avatars/' . $user->avatar)) {
try {
Storage::disk('public')->delete('avatars/' . $user->avatar);
} catch (\Exception $e) {
Log::debug($e);
}
if ($user->delete()) {
// Remove the user's avatar if they have one
// @todo This should be done on purge, not here
// if (Storage::disk('public')->exists('avatars/' . $user->avatar)) {
// try {
// Storage::disk('public')->delete('avatars/' . $user->avatar);
// } catch (\Exception $e) {
// Log::debug($e);
// }
// }
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
}
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/users/message.success.delete')));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.error.delete')));
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/users/message.cannot_delete')));
}
@@ -795,7 +811,7 @@ class UsersController extends Controller
if ($request->filled('id')) {
try {
$user = User::find($request->get('id'));
$user = User::find($request->input('id'));
$this->authorize('update', $user);
$user->two_factor_secret = null;
$user->two_factor_enrolled = 0;
@@ -98,10 +98,10 @@ class AssetCheckinController extends Controller
$asset->expected_checkin = null;
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
$asset->name = $request->get('name');
$asset->name = $request->input('name');
if ($request->filled('status_id')) {
$asset->status_id = e($request->get('status_id'));
$asset->status_id = e($request->input('status_id'));
}
// Add any custom fields that should be included in the checkout
@@ -112,11 +112,11 @@ class AssetCheckinController extends Controller
$asset->location_id = $asset->rtd_location_id;
if ($request->filled('location_id')) {
Log::debug('NEW Location ID: '.$request->get('location_id'));
$asset->location_id = $request->get('location_id');
Log::debug('NEW Location ID: '.$request->input('location_id'));
$asset->location_id = $request->input('location_id');
if ($request->get('update_default_location') == 0){
$asset->rtd_location_id = $request->get('location_id');
if ($request->input('update_default_location') == 0){
$asset->rtd_location_id = $request->input('location_id');
}
}
@@ -124,9 +124,9 @@ class AssetCheckinController extends Controller
// Handle last checkin date
$checkin_at = date('Y-m-d H:i:s');
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
if (($request->filled('checkin_at')) && ($request->input('checkin_at') != date('Y-m-d'))) {
$originalValues['action_date'] = $checkin_at;
$checkin_at = $request->get('checkin_at');
$checkin_at = $request->input('checkin_at');
}
$asset->last_checkin = $checkin_at;
@@ -145,7 +145,7 @@ class AssetCheckinController extends Controller
$acceptance->delete();
});
session()->put('redirect_option', $request->get('redirect_option'));
session()->put('redirect_option', $request->input('redirect_option'));
// Add any custom fields that should be included in the checkout
$asset->customFieldsForCheckinCheckout('display_checkin');
@@ -88,17 +88,17 @@ class AssetCheckoutController extends Controller
$asset = $this->updateAssetLocation($asset, $target);
$checkout_at = date('Y-m-d H:i:s');
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
$checkout_at = $request->get('checkout_at');
if (($request->filled('checkout_at')) && ($request->input('checkout_at') != date('Y-m-d'))) {
$checkout_at = $request->input('checkout_at');
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = $request->get('expected_checkin');
$expected_checkin = $request->input('expected_checkin');
}
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
$asset->status_id = $request->input('status_id');
}
@@ -123,9 +123,9 @@ class AssetCheckoutController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => $request->input('checkout_to_type')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->input('note'), $request->input('name'))) {
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkout.success'));
}
@@ -249,7 +249,7 @@ class AssetsController extends Controller
}
if (isset($target)) {
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location);
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->input('name'), $location);
}
$successes[] = "<a href='" . route('hardware.show', $asset) . "' style='color: white;'>" . e($asset->asset_tag) . "</a>";
@@ -266,13 +266,13 @@ class AssetsController extends Controller
}
DB::commit();
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
session()->put(['checkout_to_type' => $request->get('checkout_to_type'),
session()->put(['checkout_to_type' => $request->input('checkout_to_type'),
'other_redirect' => 'model' ]);
@@ -454,7 +454,7 @@ class AssetsController extends Controller
// Update custom fields in the database.
// FIXME: No idea why this is returning a Builder error on db_column_name.
// Need to investigate and fix. Using static method for now.
$model = AssetModel::find($request->get('model_id'));
$model = AssetModel::find($request->input('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
if ($field->element == 'checkbox' && !$request->has($field->db_column)) {
@@ -480,9 +480,9 @@ class AssetsController extends Controller
}
}
session()->put([
'redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => $request->get('redirect_option') === 'other_redirect' ? 'model' : null,
'redirect_option' => $request->input('redirect_option'),
'checkout_to_type' => $request->input('checkout_to_type'),
'other_redirect' => $request->input('redirect_option') === 'other_redirect' ? 'model' : null,
]);
@@ -552,9 +552,9 @@ class AssetsController extends Controller
*/
public function getAssetBySerial(Request $request) : RedirectResponse
{
$topsearch = ($request->get('topsearch')=="true");
$topsearch = ($request->input('topsearch')=="true");
if (!$asset = Asset::where('serial', '=', $request->get('serial'))->first()) {
if (!$asset = Asset::where('serial', '=', $request->input('serial'))->first()) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
}
$this->authorize('view', $asset);
@@ -570,8 +570,8 @@ class AssetsController extends Controller
*/
public function getAssetByTag(Request $request, $tag=null) : RedirectResponse
{
$tag = $tag ? $tag : $request->get('assetTag');
$topsearch = ($request->get('topsearch') == 'true');
$tag = $tag ? $tag : $request->input('assetTag');
$topsearch = ($request->input('topsearch') == 'true');
// Search for an exact and unique asset tag match
$assets = Asset::where('asset_tag', '=', $tag);
@@ -682,8 +682,8 @@ class AssetsController extends Controller
return (new Label())
->with('assets', collect([ $asset ]))
->with('settings', Setting::getSettings())
->with('template', request()->get('template'))
->with('offset', request()->get('offset'))
->with('template', request()->input('template'))
->with('offset', request()->input('offset'))
->with('bulkedit', false)
->with('count', 0);
}
@@ -982,7 +982,7 @@ class AssetsController extends Controller
$this->authorize('audit', Asset::class);
session()->put('redirect_option', $request->get('redirect_option'));
session()->put('redirect_option', $request->input('redirect_option'));
session()->put('other_redirect', 'audit');
@@ -588,7 +588,7 @@ class BulkAssetsController extends Controller
if ($request->session()->has('bulk_back_url')) {
$bulk_back_url = $request->session()->pull('bulk_back_url');
}
$assetIds = $request->get('ids');
$assetIds = $request->input('ids');
if(empty($assetIds)) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.delete.nothing_updated'));
@@ -652,11 +652,11 @@ class BulkAssetsController extends Controller
$target = $this->determineCheckoutTarget();
session()->put(['checkout_to_type' => $target]);
if (! is_array($request->get('selected_assets'))) {
if (! is_array($request->input('selected_assets'))) {
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans('admin/hardware/message.checkout.no_assets_selected'));
}
$asset_ids = array_filter($request->get('selected_assets'));
$asset_ids = array_filter($request->input('selected_assets'));
$assets = Asset::findOrFail($asset_ids);
@@ -692,14 +692,14 @@ class BulkAssetsController extends Controller
}
}
$checkout_at = date('Y-m-d H:i:s');
if (($request->filled('checkout_at')) && ($request->get('checkout_at') != date('Y-m-d'))) {
$checkout_at = $request->get('checkout_at');
if (($request->filled('checkout_at')) && ($request->input('checkout_at') != date('Y-m-d'))) {
$checkout_at = $request->input('checkout_at');
}
$expected_checkin = '';
if ($request->filled('expected_checkin')) {
$expected_checkin = $request->get('expected_checkin');
$expected_checkin = $request->input('expected_checkin');
}
$errors = [];
@@ -709,10 +709,10 @@ class BulkAssetsController extends Controller
// See if there is a status label passed
if ($request->filled('status_id')) {
$asset->status_id = $request->get('status_id');
$asset->status_id = $request->input('status_id');
}
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->input('note')), $asset->name, null);
//TODO - I think this logic is duplicated in the checkOut method?
if ($target->location_id != '') {
@@ -743,7 +743,7 @@ class BulkAssetsController extends Controller
public function restore(Request $request) : RedirectResponse
{
$this->authorize('update', Asset::class);
$assetIds = $request->get('ids');
$assetIds = $request->input('ids');
if (empty($assetIds)) {
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.restore.nothing_updated'));
@@ -98,7 +98,7 @@ class ComponentCheckinController extends Controller
event(new CheckoutableCheckedIn($component, $asset, auth()->user(), $request->input('note'), Carbon::now()));
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkin.success'));
@@ -80,8 +80,8 @@ class ComponentCheckoutController extends Controller
$max_to_checkout = $component->numRemaining();
// Make sure there are at least the requested number of components available to checkout
if ($max_to_checkout < $request->get('assigned_qty')) {
return redirect()->back()->withInput()->with('error', trans('admin/components/message.checkout.unavailable', ['remaining' => $max_to_checkout, 'requested' => $request->get('assigned_qty')]));
if ($max_to_checkout < $request->input('assigned_qty')) {
return redirect()->back()->withInput()->with('error', trans('admin/components/message.checkout.unavailable', ['remaining' => $max_to_checkout, 'requested' => $request->input('assigned_qty')]));
}
$validator = Validator::make($request->all(), [
@@ -115,12 +115,19 @@ class ComponentCheckoutController extends Controller
'note' => $request->input('note'),
]);
event(new CheckoutableCheckedOut($component, $asset, auth()->user(), $request->input('note')));
event(new CheckoutableCheckedOut(
$component,
$asset,
auth()->user(),
$request->input('note'),
[],
$component->checkout_qty,
));
$request->request->add(['checkout_to_type' => 'asset']);
$request->request->add(['assigned_asset' => $asset->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => $request->input('checkout_to_type')]);
return Helper::getRedirectOption($request, $component->id, 'Components')
->with('success', trans('admin/components/message.checkout.success'));
@@ -88,10 +88,10 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
@@ -168,7 +168,7 @@ class ComponentsController extends Controller
$component = $request->handleImages($component);
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
if ($component->save()) {
return Helper::getRedirectOption($request, $component->id, 'Components')
@@ -102,12 +102,20 @@ class ConsumableCheckoutController extends Controller
}
$consumable->checkout_qty = $quantity;
event(new CheckoutableCheckedOut($consumable, $user, auth()->user(), $request->input('note')));
event(new CheckoutableCheckedOut(
$consumable,
$user,
auth()->user(),
$request->input('note'),
[],
$consumable->checkout_qty,
));
$request->request->add(['checkout_to_type' => 'user']);
$request->request->add(['assigned_user' => $user->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => $request->input('checkout_to_type')]);
// Redirect to the new consumable page
@@ -98,10 +98,10 @@ class ConsumablesController extends Controller
$consumable = $request->handleImages($consumable);
}
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
@@ -175,7 +175,7 @@ class ConsumablesController extends Controller
$consumable = $request->handleImages($consumable);
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
if ($consumable->save()) {
return Helper::getRedirectOption($request, $consumable->id, 'Consumables')
+19 -19
View File
@@ -112,9 +112,9 @@ class CustomFieldsController extends Controller
if ($request->filled('custom_format')) {
$field->format = $request->get('custom_format');
$field->format = $request->input('custom_format');
} else {
$field->format = $request->get('format');
$field->format = $request->input('format');
}
if ($field->save()) {
@@ -225,34 +225,34 @@ class CustomFieldsController extends Controller
public function update(CustomFieldRequest $request, CustomField $field) : RedirectResponse
{
$this->authorize('update', CustomField::class);
$show_in_email = $request->get("show_in_email", 0);
$display_in_user_view = $request->get("display_in_user_view", 0);
$show_in_email = $request->input("show_in_email", 0);
$display_in_user_view = $request->input("display_in_user_view", 0);
// Override the display settings if the field is encrypted
if ($request->get("field_encrypted") == '1') {
if ($request->input("field_encrypted") == '1') {
$show_in_email = '0';
$display_in_user_view = '0';
}
$field->name = trim($request->get("name"));
$field->element = $request->get("element");
$field->field_values = $request->get("field_values");
$field->name = trim($request->input("name"));
$field->element = $request->input("element");
$field->field_values = $request->input("field_values");
$field->created_by = auth()->id();
$field->help_text = $request->get("help_text");
$field->help_text = $request->input("help_text");
$field->show_in_email = $show_in_email;
$field->is_unique = $request->get("is_unique", 0);
$field->is_unique = $request->input("is_unique", 0);
$field->display_in_user_view = $display_in_user_view;
$field->auto_add_to_fieldsets = $request->get("auto_add_to_fieldsets", 0);
$field->show_in_listview = $request->get("show_in_listview", 0);
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
$field->display_checkin = $request->get("display_checkin", 0);
$field->display_checkout = $request->get("display_checkout", 0);
$field->display_audit = $request->get("display_audit", 0);
$field->auto_add_to_fieldsets = $request->input("auto_add_to_fieldsets", 0);
$field->show_in_listview = $request->input("show_in_listview", 0);
$field->show_in_requestable_list = $request->input("show_in_requestable_list", 0);
$field->display_checkin = $request->input("display_checkin", 0);
$field->display_checkout = $request->input("display_checkout", 0);
$field->display_audit = $request->input("display_audit", 0);
if ($request->get('format') == 'CUSTOM REGEX') {
$field->format = $request->get('custom_format');
if ($request->input('format') == 'CUSTOM REGEX') {
$field->format = $request->input('custom_format');
} else {
$field->format = $request->get('format');
$field->format = $request->input('format');
}
if ($field->element == 'checkbox' || $field->element == 'radio'){
@@ -74,7 +74,7 @@ class CustomFieldsetsController extends Controller
{
$this->authorize('create', CustomField::class);
return view('custom_fields.fieldsets.edit')->with('item', new CustomFieldset());
return view('custom_fields.fieldsets.view')->with('custom_fieldset', new CustomFieldset());
}
/**
@@ -91,7 +91,7 @@ class CustomFieldsetsController extends Controller
$this->authorize('create', CustomField::class);
$fieldset = new CustomFieldset([
'name' => $request->get('name'),
'name' => $request->input('name'),
'created_by' => auth()->id(),
]);
@@ -127,7 +127,7 @@ class CustomFieldsetsController extends Controller
public function edit(CustomFieldset $fieldset) : View | RedirectResponse
{
$this->authorize('create', CustomField::class);
return view('custom_fields.fieldsets.edit')->with('item', $fieldset);
return view('custom_fields.fieldsets.view')->with('custom_fieldset', $fieldset);
}
/**
+17 -4
View File
@@ -44,7 +44,11 @@ class GroupsController extends Controller
$permissions = config('permissions');
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
$selectedPermissions = $request->old('permissions', $groupPermissions);
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
$users_query = User::query()
->select(['users.id', 'users.first_name', 'users.last_name', 'users.username'])
->where('show_in_list', 1)
->whereNull('deleted_at');
$users_count = $users_query->count();
$users = collect();
@@ -55,7 +59,7 @@ class GroupsController extends Controller
// Show the page
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))
->with('group', $group)
->with('associated_users', [])
->with('associated_users', collect())
->with('unselected_users', $users)
->with('all_users_count', $users_count);
}
@@ -114,8 +118,11 @@ class GroupsController extends Controller
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
$users_query = User::query()
->select(['users.id', 'users.first_name', 'users.last_name', 'users.username'])
->where('show_in_list', 1)
->whereNull('deleted_at');
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
$users_count = $users_query->count();
$associated_users = collect();
@@ -124,7 +131,13 @@ class GroupsController extends Controller
if ($users_count <= config('app.max_unpaginated_records')) {
$associated_users = $group->users()->where('show_in_list', 1)->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
// Get the unselected users
$unselected_users = User::where('show_in_list', 1)->whereNotIn('id', $associated_users->pluck('id')->toArray())->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
$unselected_users = User::query()
->select(['users.id', 'users.first_name', 'users.last_name', 'users.username'])
->where('show_in_list', 1)
->whereNotIn('id', $associated_users->pluck('id')->toArray())
->orderBy('first_name', 'asc')
->orderBy('last_name', 'asc')
->get();
}
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))
@@ -47,7 +47,7 @@ class CheckoutKitController extends Controller
*/
public function store(Request $request, $kit_id)
{
$user_id = e($request->get('user_id'));
$user_id = e($request->input('user_id'));
if (is_null($user = User::find($user_id))) {
return redirect()->back()->with('error', trans('admin/users/message.user_not_found'));
}
+1 -1
View File
@@ -81,7 +81,7 @@ class LabelsController extends Controller
$settings = Setting::getSettings();
if (request()->has('settings')) {
$overrides = request()->get('settings');
$overrides = request()->input('settings');
foreach ($overrides as $key => $value) {
$settings->$key = $value;
}
@@ -97,8 +97,8 @@ class LicenseCheckinController extends Controller
$licenseSeat->unreassignable_seat = true;
}
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($request->get('redirect_option') === 'target'){
session()->put(['redirect_option' => $request->input('redirect_option')]);
if ($request->input('redirect_option') === 'target'){
session()->put(['checkout_to_type' => 'user']);
}
@@ -96,13 +96,13 @@ class LicenseCheckoutController extends Controller
session()->put(['checkout_to_type' => 'asset']);
$checkoutTarget = $this->checkoutToAsset($licenseSeat);
$request->request->add(['assigned_asset' => $checkoutTarget->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'asset']);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => 'asset']);
} elseif ($request->filled('assigned_to')) {
session()->put(['checkout_to_type' => 'user']);
$checkoutTarget = $this->checkoutToUser($licenseSeat);
$request->request->add(['assigned_user' => $checkoutTarget->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'user']);
session()->put(['redirect_option' => $request->input('redirect_option'), 'checkout_to_type' => 'user']);
}
@@ -102,10 +102,10 @@ class LicensesController extends Controller
$license->created_by = auth()->id();
$license->min_amt = $request->input('min_amt');
if($request->get('redirect_option') === 'back'){
if($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
if ($license->save()) {
@@ -183,7 +183,7 @@ class LicensesController extends Controller
$license->category_id = $request->input('category_id');
$license->min_amt = $request->input('min_amt');
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
if ($license->save()) {
return Helper::getRedirectOption($request, $license->id, 'Licenses')
+6 -4
View File
@@ -818,7 +818,7 @@ class ReportsController extends Controller
}
if ($request->filled('eol')) {
$row[] = ($asset->purchase_date != '') ? $asset->asset_eol_date : '';
$row[] = ($asset->asset_eol_date != '') ? $asset->asset_eol_date : '';
}
if ($request->filled('warranty')) {
@@ -1228,12 +1228,14 @@ class ReportsController extends Controller
];
$mailable= $lookup[get_class($acceptance->checkoutable)];
return new $mailable($acceptance->checkoutable,
return new $mailable(
$acceptance->checkoutable,
$acceptance->checkedOutTo ?? $acceptance->assignedTo,
$logItem->adminuser,
$acceptance,
$acceptance->note);
$acceptance->note,
firstTimeSending: false,
);
}
/**
* sentAssetAcceptanceReminder
+1 -1
View File
@@ -114,7 +114,7 @@ class SettingsController extends Controller
$setting->email_domain = $request->input('email_domain');
$setting->email_format = $request->input('email_format');
$setting->username_format = $request->input('username_format');
$setting->require_accept_signature = $request->input('require_accept_signature');
$setting->require_accept_signature = $request->input('require_accept_signature', '0');
$setting->show_assigned_assets = $request->input('show_assigned_assets', '0');
if (! config('app.lock_passwords')) {
$setting->login_note = $request->input('login_note');
@@ -56,7 +56,7 @@ class UploadedFilesController extends Controller
foreach ($request->file('file') as $file) {
$file_name = $request->handleFile(self::$map_storage_path[$object_type], self::$map_file_prefix[$object_type].'-'.$object->id, $file);
$files[] = $file_name;
$object->logUpload($file_name, $request->get('notes'));
$object->logUpload($file_name, $request->input('notes'));
}
$files = Actionlog::select('action_logs.*')->where('action_type', '=', 'uploaded')
@@ -169,6 +169,7 @@ class BulkUsersController extends Controller
->conditionallyAddItem('remote')
->conditionallyAddItem('ldap_import')
->conditionallyAddItem('activated')
->conditionallyAddItem('display_name')
->conditionallyAddItem('start_date')
->conditionallyAddItem('end_date')
->conditionallyAddItem('city')
@@ -214,6 +215,10 @@ class BulkUsersController extends Controller
$this->update_array['locale'] = null;
}
if ($request->input('null_display_name')=='1') {
$this->update_array['display_name'] = null;
}
if (! $manager_conflict) {
$this->conditionallyAddItem('manager_id');
}
@@ -229,8 +234,11 @@ class BulkUsersController extends Controller
// Only sync groups if groups were selected
if ($request->filled('groups')) {
foreach ($users as $user) {
$user->groups()->sync($request->input('groups'));
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->groups()->sync($request->input('groups'));
}
}
}
@@ -312,7 +320,9 @@ class BulkUsersController extends Controller
foreach ($users as $user) {
$user->accessories()->sync([]);
if ($request->input('delete_user')=='1') {
$user->delete();
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->delete();
}
}
}
+13 -8
View File
@@ -56,7 +56,7 @@ class UsersController extends Controller
public function create(Request $request)
{
$this->authorize('create', User::class);
$groups = Group::pluck('name', 'id');
$groups = Group::orderBy('name', 'asc')->pluck('name', 'id');
$userGroups = collect();
@@ -130,10 +130,10 @@ class UsersController extends Controller
// we have to invoke the form request here to handle image uploads
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
if ($request->get('redirect_option') === 'back'){
if ($request->input('redirect_option') === 'back'){
session()->put(['redirect_option' => 'index']);
} else {
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
}
@@ -151,7 +151,9 @@ class UsersController extends Controller
}
if ($request->filled('groups')) {
$user->groups()->sync($request->input('groups'));
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->groups()->sync($request->input('groups'));
}
} else {
$user->groups()->sync([]);
}
@@ -199,7 +201,7 @@ class UsersController extends Controller
}
$permissions = config('permissions');
$groups = Group::pluck('name', 'id');
$groups = Group::orderBy('name', 'asc')->pluck('name', 'id');
$userGroups = $user->groups()->pluck('name', 'id');
$user->permissions = $user->decodePermissions();
@@ -325,7 +327,7 @@ class UsersController extends Controller
// Handle uploaded avatar
app(ImageUploadRequest::class)->handleImages($user, 600, 'avatar', 'avatars', 'avatar');
session()->put(['redirect_option' => $request->get('redirect_option')]);
session()->put(['redirect_option' => $request->input('redirect_option')]);
if ($user->save()) {
// Redirect to the user page
@@ -351,10 +353,13 @@ class UsersController extends Controller
if ($user = User::find($id)) {
$this->authorize('delete', $user);
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
if ($user->delete()) {
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.delete'));
if ($user->delete()) {
return redirect()->route('users.index')->with('success', trans('admin/users/message.success.delete'));
}
}
return redirect()->route('users.index')->with('error', trans('admin/users/message.cannot_delete'));
}
return redirect()->route('users.index')->with('error', trans('admin/users/message.user_not_found'));
+2 -2
View File
@@ -62,8 +62,8 @@ class ItemImportRequest extends FormRequest
}
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
->setCreatedBy(auth()->id())
->setUpdating($this->get('import-update'))
->setShouldNotify($this->get('send-welcome'))
->setUpdating($this->input('import-update'))
->setShouldNotify($this->input('send-welcome'))
->setUsernameFormat('firstname.lastname')
->setFieldMappings($fieldMappings);
$importer->import();
+1 -1
View File
@@ -44,7 +44,7 @@ class SaveUserRequest extends FormRequest
case 'POST':
$rules['first_name'] = 'required|string|min:1';
$rules['username'] = 'required_unless:ldap_import,1|string|min:1';
if ($this->request->get('ldap_import') == false) {
if ($this->input('ldap_import') == false) {
$rules['password'] = Setting::passwordComplexityRulesSaving('store').'|confirmed';
}
break;
@@ -19,6 +19,7 @@ class StoreAccessoryRequest extends ImageUploadRequest
public function prepareForValidation(): void
{
parent::prepareForValidation();
if ($this->category_id) {
if ($category = Category::find($this->category_id)) {
@@ -19,6 +19,7 @@ class StoreConsumableRequest extends ImageUploadRequest
public function prepareForValidation(): void
{
parent::prepareForValidation();
if ($this->category_id) {
if ($category = Category::find($this->category_id)) {
@@ -1,6 +1,7 @@
<?php
namespace App\Http\Transformers;
use App\Enums\ActionType;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Models\Actionlog;
@@ -80,7 +81,7 @@ class ActionlogsTransformer
// this is a custom field
if (str_starts_with($fieldname, '_snipeit_')) {
foreach ($custom_fields as $custom_field) {
if ($custom_field->db_column == $fieldname) {
@@ -185,9 +186,9 @@ class ActionlogsTransformer
'name' => e($actionlog->target->display_name) ?? null,
'type' => e($actionlog->targetType()),
] : null,
'quantity' => $this->getQuantity($actionlog),
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedownInline($actionlog->note): null,
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'signature_file' => (($actionlog->accept_signature) && Storage::exists('private_uploads/signatures/'.$actionlog->accept_signature)) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
'remote_ip' => e($actionlog->remote_ip) ?? null,
'user_agent' => e($actionlog->user_agent) ?? null,
@@ -336,6 +337,26 @@ class ActionlogsTransformer
}
private function getQuantity(Actionlog $actionlog): ?int
{
if (!$actionlog->quantity) {
return null;
}
// only a few action types will have a quantity we are interested in.
if (!in_array($actionlog->action_type, [
ActionType::Checkout->value,
ActionType::Accepted->value,
ActionType::Declined->value,
ActionType::CheckinFrom->value,
ActionType::AddSeats->value,
ActionType::DeleteSeats->value,
])) {
return null;
}
return (int) $actionlog->quantity;
}
}
@@ -104,6 +104,7 @@ class AssetsTransformer
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date, 'date'),
'deleted_at' => Helper::getFormattedDateObject($asset->deleted_at, 'datetime'),
'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
'first_checkout' => Helper::getFormattedDateObject($asset->first_checkout_at, 'datetime'),
'age' => $asset->purchase_date ? $asset->purchase_date->locale(app()->getLocale())->diffForHumans() : '',
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
'last_checkin' => Helper::getFormattedDateObject($asset->last_checkin, 'datetime'),
+1 -1
View File
@@ -101,7 +101,7 @@ class UsersTransformer
$permissions_array['available_actions'] = [
'update' => (Gate::allows('update', User::class) && ($user->deleted_at == '')),
'delete' => $user->isDeletable(),
'delete' => ($user->isDeletable() && (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo'))),
'clone' => (Gate::allows('create', User::class) && ($user->deleted_at == '')),
'restore' => (Gate::allows('create', User::class) && ($user->deleted_at != '')),
];
+3 -3
View File
@@ -85,9 +85,9 @@ class AssetImporter extends ItemImporter
if ($this->findCsvMatch($row, 'id')!='') {
// Override asset if an ID was given
\Log::debug('Finding asset by ID: '.$this->findCsvMatch($row, 'id'));
$asset = Asset::find($this->findCsvMatch($row, 'id'));
$asset = Asset::with('assignedTo')->find($this->findCsvMatch($row, 'id'));
} else {
$asset = Asset::where(['asset_tag'=> (string) $asset_tag])->first();
$asset = Asset::with('assignedTo')->where(['asset_tag' => (string) $asset_tag])->first();
}
if ($asset) {
@@ -203,7 +203,7 @@ class AssetImporter extends ItemImporter
if (isset($target) && ($target !== false)) {
if (!is_null($asset->assigned_to)){
if ($asset->assigned_to != $target->id) {
event(new CheckoutableCheckedIn($asset, User::find($asset->assigned_to), auth()->user(), 'Checkin from CSV Importer', $checkin_date));
event(new CheckoutableCheckedIn($asset, $asset->assigned, auth()->user(), 'Checkin from CSV Importer', $checkin_date));
}
}
+2 -2
View File
@@ -10,6 +10,7 @@ use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Auth;
/**
* This is ONLY used for the User Import. When we are importing users
@@ -102,8 +103,7 @@ class UserImporter extends ItemImporter
$this->log('Updating User');
// Todo - check that this works
if (!Gate::allows('canEditAuthFields', $user)) {
if (Auth::check() && (!Gate::allows('canEditAuthFields', $user))) {
unset($user->username);
unset($user->email);
unset($user->password);
+16 -3
View File
@@ -128,11 +128,18 @@ class CheckoutableListener
->notify($this->getCheckoutNotification($event, $acceptance));
}
} catch (ClientException $e) {
$status = optional($e->getResponse()->getStatusCode());
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_channel_not_found'));
} else {
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
if ($status >= 500 || $status === null) {
Log::error(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
} else {
Log::warning("ClientException caught during checkin notification: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
}
}
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
} catch (Exception $e) {
@@ -224,12 +231,18 @@ class CheckoutableListener
->notify($this->getCheckinNotification($event));
}
} catch (ClientException $e) {
$status = optional($e->getResponse()->getStatusCode());
if (strpos($e->getMessage(), 'channel_not_found') !== false) {
Log::warning(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_channel_not_found'));
} else {
Log::error("ClientException caught during checkin notification: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
if ($status >= 500 || $status === null) {
Log::error(Setting::getSettings()->webhook_selected . " notification failed: " . $e->getMessage());
} else {
Log::warning("ClientException caught during checkin notification: " . $e->getMessage());
return redirect()->back()->with('warning', ucfirst(Setting::getSettings()->webhook_selected) . trans('admin/settings/message.webhook.webhook_fail'));
}
}
} catch (Exception $e) {
Log::warning(ucfirst(Setting::getSettings()->webhook_selected) . ' webhook notification failed:', [
+12 -1
View File
@@ -47,7 +47,13 @@ class LogListener
*/
public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event)
{
$event->checkoutable->logCheckout($event->note, $event->checkedOutTo, $event->checkoutable->last_checkout, $event->originalValues);
$event->checkoutable->logCheckout(
$event->note,
$event->checkedOutTo,
$event->checkoutable->last_checkout,
$event->originalValues,
$event->quantity
);
}
/**
@@ -66,6 +72,9 @@ class LogListener
$logaction->note = $event->acceptance->note;
$logaction->action_type = 'accepted';
$logaction->action_date = $event->acceptance->accepted_at;
$logaction->quantity = $event->acceptance->qty ?? 1;
$logaction->created_by = auth()->user()->id;
// TODO: log the actual license seat that was checked out
if ($event->acceptance->checkoutable instanceof LicenseSeat) {
@@ -84,6 +93,8 @@ class LogListener
$logaction->note = $event->acceptance->note;
$logaction->action_type = 'declined';
$logaction->action_date = $event->acceptance->declined_at;
$logaction->quantity = $event->acceptance->qty ?? 1;
$logaction->created_by = auth()->user()->id;
// TODO: log the actual license seat that was checked out
if ($event->acceptance->checkoutable instanceof LicenseSeat) {
+3 -2
View File
@@ -409,14 +409,13 @@ class Importer extends Component
'category' => trans('general.category'),
'eol' => trans('general.eol'),
'fieldset' => trans('admin/models/general.fieldset'),
'item_name' => trans('general.item_name_var', ['item' => trans('general.asset_model')]),
'name' => trans('general.item_name_var', ['item' => trans('general.asset_model')]),
'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'),
'require_serial' => trans('admin/hardware/general.require_serial'),
];
// "real fieldnames" to a list of aliases for that field
@@ -425,6 +424,8 @@ class Importer extends Component
[
'item name',
'asset name',
'model name',
'asset model name',
'accessory name',
'user name',
'consumable name',
+20 -4
View File
@@ -18,10 +18,12 @@ class CheckoutAccessoryMail extends BaseMailable
{
use Queueable, SerializesModels;
private bool $firstTimeSending;
/**
* Create a new message instance.
*/
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $accessory;
$this->admin = $checkedOutBy;
@@ -29,6 +31,7 @@ class CheckoutAccessoryMail extends BaseMailable
$this->checkout_qty = $accessory->checkout_qty;
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->firstTimeSending = $firstTimeSending;
$this->settings = Setting::getSettings();
}
@@ -41,7 +44,7 @@ class CheckoutAccessoryMail extends BaseMailable
return new Envelope(
from: $from,
subject: trans_choice('mail.Accessory_Checkout_Notification', $this->checkout_qty),
subject: $this->getSubject(),
);
}
@@ -88,14 +91,18 @@ class CheckoutAccessoryMail extends BaseMailable
return trans_choice('mail.new_item_checked_location', $this->checkout_qty, ['location' => $this->target->name]);
}
if ($this->requiresAcceptance()) {
if ($this->firstTimeSending && $this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked_with_acceptance', $this->checkout_qty);
}
if (!$this->requiresAcceptance()) {
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked', $this->checkout_qty);
}
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
return trans('mail.recent_item_checked');
}
// we shouldn't get here but let's send a default message just in case
return trans('new_item_checked');
}
@@ -113,4 +120,13 @@ class CheckoutAccessoryMail extends BaseMailable
{
return [];
}
private function getSubject(): string
{
if ($this->firstTimeSending) {
return trans_choice('mail.Accessory_Checkout_Notification', $this->checkout_qty);
}
return trans('mail.unaccepted_asset_reminder');
}
}
+38 -2
View File
@@ -15,10 +15,12 @@ class CheckoutConsumableMail extends BaseMailable
{
use Queueable, SerializesModels;
private bool $firstTimeSending;
/**
* Create a new message instance.
*/
public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $consumable;
$this->admin = $checkedOutBy;
@@ -26,6 +28,7 @@ class CheckoutConsumableMail extends BaseMailable
$this->target = $checkedOutTo;
$this->acceptance = $acceptance;
$this->qty = $consumable->checkout_qty;
$this->firstTimeSending = $firstTimeSending;
$this->settings = Setting::getSettings();
}
@@ -39,7 +42,7 @@ class CheckoutConsumableMail extends BaseMailable
return new Envelope(
from: $from,
subject: trans('mail.Confirm_consumable_delivery'),
subject: $this->getSubject(),
);
}
@@ -65,6 +68,7 @@ class CheckoutConsumableMail extends BaseMailable
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'qty' => $this->qty,
'introduction_line' => $this->introductionLine(),
]
);
}
@@ -78,4 +82,36 @@ class CheckoutConsumableMail extends BaseMailable
{
return [];
}
private function getSubject(): string
{
if ($this->firstTimeSending) {
return trans('mail.Confirm_consumable_delivery');
}
return trans('mail.unaccepted_asset_reminder');
}
private function introductionLine()
{
if ($this->firstTimeSending && $this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked_with_acceptance', $this->qty);
}
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked', $this->qty);
}
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
return trans('mail.recent_item_checked');
}
// we shouldn't get here but let's send a default message just in case
return trans('new_item_checked');
}
private function requiresAcceptance(): int|bool
{
return method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
}
}
+38 -2
View File
@@ -16,10 +16,12 @@ class CheckoutLicenseMail extends BaseMailable
{
use Queueable, SerializesModels;
private bool $firstTimeSending;
/**
* Create a new message instance.
*/
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note)
public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note, bool $firstTimeSending = true)
{
$this->item = $licenseSeat;
$this->admin = $checkedOutBy;
@@ -27,6 +29,7 @@ class CheckoutLicenseMail extends BaseMailable
$this->acceptance = $acceptance;
$this->settings = Setting::getSettings();
$this->target = $checkedOutTo;
$this->firstTimeSending = $firstTimeSending;
if($this->target instanceof User){
$this->target = $this->target->display_name;
@@ -45,7 +48,7 @@ class CheckoutLicenseMail extends BaseMailable
return new Envelope(
from: $from,
subject: trans('mail.Confirm_license_delivery'),
subject: $this->getSubject(),
);
}
@@ -69,6 +72,7 @@ class CheckoutLicenseMail extends BaseMailable
'eula' => $eula,
'req_accept' => $req_accept,
'accept_url' => $accept_url,
'introduction_line' => $this->introductionLine(),
]
);
}
@@ -82,4 +86,36 @@ class CheckoutLicenseMail extends BaseMailable
{
return [];
}
private function getSubject(): string
{
if ($this->firstTimeSending) {
return trans('mail.Confirm_license_delivery');
}
return trans('mail.unaccepted_asset_reminder');
}
private function introductionLine(): string
{
if ($this->firstTimeSending && $this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked_with_acceptance', 1);
}
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
return trans_choice('mail.new_item_checked', 1);
}
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
return trans('mail.recent_item_checked');
}
// we shouldn't get here but let's send a default message just in case
return trans('new_item_checked');
}
private function requiresAcceptance(): int|bool
{
return method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ class Accessory extends SnipeModel
* Accessory validation rules
*/
public $rules = [
'name' => 'required|min:3|max:255',
'name' => 'required|max:255',
'qty' => 'required|integer|min:1',
'category_id' => 'required|integer|exists:categories,id',
'company_id' => 'integer|nullable',
+3 -2
View File
@@ -20,6 +20,7 @@ use Watson\Validating\ValidatingTrait;
*/
class AccessoryCheckout extends Model
{
use HasFactory;
use Searchable;
protected $fillable = [
@@ -41,7 +42,7 @@ class AccessoryCheckout extends Model
*/
public function accessory()
{
return $this->hasOne(Accessory::class, 'id', 'accessory_id');
return $this->belongsTo(Accessory::class);
}
public function accessories()
@@ -57,7 +58,7 @@ class AccessoryCheckout extends Model
*/
public function adminuser()
{
return $this->hasOne(\App\Models\User::class, 'id', 'created_by');
return $this->belongsTo(User::class, 'created_by');
}
/**
+1 -1
View File
@@ -375,7 +375,7 @@ class Actionlog extends SnipeModel
}
// Show as negative number if the next audit date is before the audit date we're looking at
if ($this->created_at > $override_default_next) {
if ($this->created_at->toDateString() > $override_default_next->toDateString()) {
$next_audit_days = '-'.$next_audit_days;
}
+1 -1
View File
@@ -570,7 +570,7 @@ class Asset extends Depreciable
if (is_array(request()->input($field->db_column))) {
$this->{$field->db_column} = Crypt::encrypt(implode(', ', request()->input($field->db_column)));
} else {
$this->{$field->db_column} = Crypt::encrypt(request()->get($field->db_column));
$this->{$field->db_column} = Crypt::encrypt(request()->input($field->db_column));
}
}
+5 -1
View File
@@ -85,7 +85,7 @@ class CheckoutAcceptance extends Model
/**
* The user that the checkoutable was checked out to
*
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function assignedTo()
{
@@ -186,6 +186,10 @@ class CheckoutAcceptance extends Model
);
}
protected function scopeHasFiles(Builder $query) {
return $query->whereNotNull('signature_filename')->orWhereNotNull('stored_eula_file');
}
public function generateAcceptancePdf($data, $pdf_filename) {
// set some language dependent data:
+2 -2
View File
@@ -27,10 +27,10 @@ final class Company extends SnipeModel
// Declare the rules for the model validation
protected $rules = [
'name' => 'required|min:1|max:255|unique:companies,name',
'name' => 'required|max:255|unique:companies,name',
'fax' => 'min:7|max:35|nullable',
'phone' => 'min:7|max:35|nullable',
'email' => 'email|max:150|nullable',
'email' => 'email|max:150|nullable',
];
protected $presenter = \App\Presenters\CompanyPresenter::class;
+3 -3
View File
@@ -36,7 +36,7 @@ class Component extends SnipeModel
* Category validation rules
*/
public $rules = [
'name' => 'required|min:3|max:191',
'name' => 'required|max:191',
'qty' => 'required|integer|min:1',
'category_id' => 'required|integer|exists:categories,id',
'supplier_id' => 'nullable|integer|exists:suppliers,id',
@@ -269,7 +269,7 @@ class Component extends SnipeModel
// is *not* null - so we don't have to keep recalculating for un-checked-out components
// 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->uncontrainedAssets()->sum('assigned_qty') ?? 0;
$this->sum_unconstrained_assets = $this->unconstrainedAssets()->sum('assigned_qty') ?? 0;
}
return $this->sum_unconstrained_assets;
}
@@ -280,7 +280,7 @@ class Component extends SnipeModel
*
* This allows us to get the assets with assigned components without the company restriction
*/
public function uncontrainedAssets()
public function unconstrainedAssets()
{
return $this->belongsToMany(\App\Models\Asset::class, 'components_assets')
+1 -1
View File
@@ -42,7 +42,7 @@ class Consumable extends SnipeModel
* Category validation rules
*/
public $rules = [
'name' => 'required|min:3|max:255',
'name' => 'required|max:255',
'qty' => 'required|integer|min:0|max:99999',
'category_id' => 'required|integer',
'company_id' => 'integer|nullable',
+6 -1
View File
@@ -2,6 +2,8 @@
namespace App\Models;
use App\Models\Traits\Loggable;
use App\Presenters\Presentable;
use App\Rules\AlphaEncrypted;
use App\Rules\BooleanEncrypted;
use App\Rules\DateEncrypted;
@@ -18,10 +20,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
class CustomFieldset extends Model
class CustomFieldset extends SnipeModel
{
use HasFactory;
use ValidatingTrait;
use Presentable;
protected $presenter = \App\Presenters\CustomFieldsetPresenter::class;
protected $guarded = ['id'];
+12 -1
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Gate;
use Watson\Validating\ValidatingTrait;
class Depreciation extends SnipeModel
@@ -15,7 +16,7 @@ class Depreciation extends SnipeModel
use Presentable;
// Declare the rules for the form validation
protected $rules = [
'name' => 'required|min:3|max:255|unique:depreciations,name',
'name' => 'required|max:255|unique:depreciations,name',
'months' => 'required|max:3600|integer',
];
@@ -52,6 +53,16 @@ class Depreciation extends SnipeModel
*/
protected $searchableRelations = [];
public function isDeletable()
{
return Gate::allows('delete', $this)
&& (($this->assets_count ?? $this->assets()->count()) === 0)
&& (($this->licenses_count ?? $this->licenses()->count()) === 0)
&& (($this->models_count ?? $this->models()->count()) === 0)
&& ($this->deleted_at == '');
}
/**
* Establishes the depreciation -> models relationship
*
+1 -1
View File
@@ -13,7 +13,7 @@ class Group extends SnipeModel
protected $table = 'permission_groups';
public $rules = [
'name' => 'required|min:2|max:255|unique',
'name' => 'required|max:255|unique',
];
protected $fillable = [
+22 -20
View File
@@ -3,6 +3,8 @@
namespace App\Models\Labels\Sheets\Avery;
use App\Helpers\Helper;
class L4736_A extends L4736
{
private const BARCODE_MARGIN = 1.80;
@@ -96,38 +98,38 @@ class L4736_A extends L4736
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
$fields = $record->get('fields');
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
$baseHeight = $fieldCount * $perFieldHeight;
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true, 0
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];
}
}
+22 -27
View File
@@ -2,6 +2,8 @@
namespace App\Models\Labels\Sheets\Avery;
use App\Helpers\Helper;
class L6009_A extends L6009
{
private const BARCODE_MARGIN = 1.80;
@@ -60,43 +62,36 @@ class L6009_A extends L6009
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true, 0
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];
}
}
}
+62 -24
View File
@@ -3,16 +3,18 @@
namespace App\Models\Labels\Sheets\Avery;
use App\Helpers\Helper;
class L7163_A extends L7163
{
private const BARCODE_MARGIN = 1.80;
private const TAG_SIZE = 4.80;
private const TITLE_SIZE = 5.00;
private const TITLE_MARGIN = 1.80;
private const LABEL_SIZE = 2.35;
private const TITLE_MARGIN = .75;
private const LABEL_SIZE = 3.35;
private const LABEL_MARGIN = - 0.30;
private const FIELD_SIZE = 4.80;
private const FIELD_MARGIN = 0.30;
private const FIELD_MARGIN = 0.20;
public function getUnit()
{
@@ -74,17 +76,7 @@ class L7163_A extends L7163
$currentX = $pa->x1;
$currentY = $pa->y1;
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', '', self::TITLE_SIZE, 'C',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
$barcodeSize = $pa->h - self::TITLE_SIZE - self::TITLE_MARGIN - self::TAG_SIZE;
$barcodeSize = $pa->h - self::TAG_SIZE;
if ($record->has('barcode2d')) {
static::writeText(
@@ -108,25 +100,71 @@ class L7163_A extends L7163
$usableWidth, self::TAG_SIZE, true, 0
);
}
$title = $record->has('title') ? $record->get('title') : null;
$fields = $record->get('fields');
foreach ($record->get('fields') as $field) {
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
title: $title,
baseTitleSize: self::TITLE_SIZE,
baseTitleMargin: self::TITLE_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
if ($field_layout['hasTitle']) {
static::writeText(
$pdf, $field['label'],
$pdf, $title,
$currentX, $currentY,
'freesans', '', self::LABEL_SIZE, 'L',
$usableWidth, self::LABEL_SIZE, true, 0
'freesans', 'b', $field_layout['titleSize'], 'L',
$usableWidth, $field_layout['titleSize'], true, 0
);
$currentY += $field_layout['titleAdvance'];
}
foreach ($fields as $field) {
$rawLabel = $field['label'] ?? null;
$value = (string)($field['value'] ?? '');
// No label: value takes the whole row
if (!is_string($rawLabel) || trim($rawLabel) === '') {
static::writeText(
$pdf, $value,
$currentX, $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$usableWidth, $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $field_layout['rowAdvance'];
continue;
}
$labelText = rtrim($field['label'], ':') . ':';
static::writeText(
$pdf, $labelText,
$currentX, $currentY,
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true,
);
$currentY += self::LABEL_SIZE + self::LABEL_MARGIN;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', self::FIELD_SIZE, 'L',
$usableWidth, self::FIELD_SIZE, true, 0, 0.5
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += self::FIELD_SIZE + self::FIELD_MARGIN;
$currentY += $field_layout['rowAdvance'];;
}
}
}
+22 -24
View File
@@ -2,6 +2,8 @@
namespace App\Models\Labels\Tapes\Brother;
use App\Helpers\Helper;
class TZe_241 extends TZe_18mm
{
private const LABEL_SIZE = 5.0;
@@ -55,40 +57,36 @@ class TZe_241 extends TZe_18mm
$fields = $record->get('fields') ?? [];
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true, 0
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];
}
}
}
+31 -39
View File
@@ -2,6 +2,8 @@
namespace App\Models\Labels\Tapes\Brother;
use App\Helpers\Helper;
class TZe_24mm_E extends TZe_24mm
{
private const BARCODE_MARGIN = 1.75;
@@ -71,49 +73,39 @@ class TZe_24mm_E extends TZe_24mm
}
$fields = $record->get('fields');
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
$labelSize = self::LABEL_SIZE * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true, 0
);
foreach ($fields as $field) {
// Write label and value on the same line
// Calculate label width with proportional character spacing
$labelWidth = $pdf->GetStringWidth($field['label'], 'freesans', '', $labelSize);
$charCount = strlen($field['label']);
$spacingPerChar = 0.5;
$totalSpacing = $charCount * $spacingPerChar;
$adjustedWidth = $labelWidth + $totalSpacing;
static::writeText(
$pdf, $field['value'],
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $field_layout['rowAdvance'];
}
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', 'B', $labelSize, 'L',
$adjustedWidth, $labelSize, true, 0, $spacingPerChar
);
static::writeText(
$pdf, $field['value'],
$currentX + $adjustedWidth + 2, $currentY,
'freesans', 'B', $fieldSize, 'L',
$usableWidth - $adjustedWidth - 2, $fieldSize, true, 0, 0.3
);
$currentY += max($labelSize, $fieldSize) +$fieldMargin;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
@@ -3,6 +3,8 @@
namespace App\Models\Labels\Tapes\Dymo;
use App\Helpers\Helper;
class LabelWriter_11354 extends LabelWriter
{
private const BARCODE1D_HEIGHT = 3.00;
@@ -94,57 +96,80 @@ class LabelWriter_11354 extends LabelWriter
}
// Right column
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'b', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
$title = $record->has('title') ? $record->get('title') : null;
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE
- self::BARCODE_MARGIN;
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
$maxFields = $this->getSupportFields();
$fields = collect($fields);
if ($title) {
$maxFields = max(0, $maxFields - 1); // title consumes one rows worth of space
}
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
$fields = $fields->take($maxFields)->values();
foreach ($fields as $field) {
$usableHeight = $pa->h
- self::TAG_SIZE // bottom tag text
- self::BARCODE_MARGIN; // gap between fields and 1D
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
title: $title,
baseTitleSize: self::TITLE_SIZE,
baseTitleMargin: self::TITLE_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
if ($field_layout['hasTitle']) {
static::writeText(
$pdf, $field['label'],
$pdf, $title,
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', 'b', $field_layout['titleSize'], 'L',
$usableWidth, $field_layout['titleSize'], true, 0
);
$currentY += $field_layout['titleAdvance'];
}
foreach ($fields as $field) {
$rawLabel = $field['label'] ?? null;
$value = (string)($field['value'] ?? '');
// No label: value takes the whole row
if (!is_string($rawLabel) || trim($rawLabel) === '') {
static::writeText(
$pdf, $value,
$currentX, $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$usableWidth, $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $field_layout['rowAdvance'];
continue;
}
$labelText = rtrim($field['label'], ':') . ':';
static::writeText(
$pdf, $labelText,
$currentX, $currentY,
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true,
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];;
}
}
@@ -3,6 +3,8 @@
namespace App\Models\Labels\Tapes\Dymo;
use App\Helpers\Helper;
class LabelWriter_1933081 extends LabelWriter
{
private const BARCODE_MARGIN = 1.80;
@@ -79,59 +81,82 @@ class LabelWriter_1933081 extends LabelWriter
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'b', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
$title = $record->has('title') ? $record->get('title') : null;
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
$maxFields = $this->getSupportFields();
$fields = collect($fields);
if ($title) {
$maxFields = max(0, $maxFields - 1); // title consumes one rows worth of space
}
$fields = $fields->take($maxFields)->values();
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE // bottom tag text
- self::BARCODE_MARGIN; // gap between fields and 1D
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
title: $title,
baseTitleSize: self::TITLE_SIZE,
baseTitleMargin: self::TITLE_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
if ($field_layout['hasTitle']) {
static::writeText(
$pdf, $field['label'],
$pdf, $title,
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', 'b', $field_layout['titleSize'], 'L',
$usableWidth, $field_layout['titleSize'], true, 0
);
$currentY += $field_layout['titleAdvance'];
}
foreach ($fields as $field) {
$rawLabel = $field['label'] ?? null;
$value = (string)($field['value'] ?? '');
// No label: value takes the whole row
if (!is_string($rawLabel) || trim($rawLabel) === '') {
static::writeText(
$pdf, $value,
$currentX, $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$usableWidth, $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $field_layout['rowAdvance'];
continue;
}
$labelText = rtrim($field['label'], ':') . ':';
static::writeText(
$pdf, $labelText,
$currentX, $currentY,
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true,
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];;
}
if ($record->has('barcode1d')) {
@@ -3,6 +3,8 @@
namespace App\Models\Labels\Tapes\Dymo;
use App\Helpers\Helper;
class LabelWriter_2112283 extends LabelWriter
{
private const BARCODE_MARGIN = 1.80;
@@ -81,57 +83,80 @@ class LabelWriter_2112283 extends LabelWriter
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
}
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'b', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
$title = $record->has('title') ? $record->get('title') : null;
$fields = $record->get('fields');
$maxFields = $this->getSupportFields();
$fields = collect($fields);
if ($title) {
$maxFields = max(0, $maxFields - 1); // title consumes one rows worth of space
}
$fields = $record->get('fields');
// Below rescales the size of the field box to fit, it feels like it could/should be abstracted one class above
// to be usable on other labels but im unsure of how to implement that, since it uses a lot of private
// constants.
$fields = $fields->take($maxFields)->values();
// Figure out how tall the label fields wants to be
$fieldCount = count($fields);
$perFieldHeight = (self::LABEL_SIZE + self::LABEL_MARGIN)
+ (self::FIELD_SIZE + self::FIELD_MARGIN);
$usableHeight = $pa->h
- self::TAG_SIZE // bottom tag text
- self::BARCODE_MARGIN; // gap between fields and 1D
$baseHeight = $fieldCount * $perFieldHeight;
// If it doesn't fit in the available height, scale everything down
$scale = 1.0;
if ($baseHeight > $usableHeight && $baseHeight > 0) {
$scale = $usableHeight / $baseHeight;
}
$field_layout = Helper::labelFieldLayoutScaling(
pdf: $pdf,
fields: $fields,
currentX: $currentX,
usableWidth: $usableWidth,
usableHeight: $usableHeight,
baseLabelSize: self::LABEL_SIZE,
baseFieldSize: self::FIELD_SIZE,
baseFieldMargin: self::FIELD_MARGIN,
title: $title,
baseTitleSize: self::TITLE_SIZE,
baseTitleMargin: self::TITLE_MARGIN,
baseLabelPadding: 1.5,
baseGap: 1.5,
maxScale: 1.8,
labelFont: 'freesans',
);
$labelSize = self::LABEL_SIZE * $scale;
$labelMargin = self::LABEL_MARGIN * $scale;
$fieldSize = self::FIELD_SIZE * $scale;
$fieldMargin = self::FIELD_MARGIN * $scale;
foreach ($fields as $field) {
if ($field_layout['hasTitle']) {
static::writeText(
$pdf, $field['label'],
$pdf, $title,
$currentX, $currentY,
'freesans', '', $labelSize, 'L',
$usableWidth, $labelSize, true, 0
'freesans', 'b', $field_layout['titleSize'], 'L',
$usableWidth, $field_layout['titleSize'], true, 0
);
$currentY += $field_layout['titleAdvance'];
}
foreach ($fields as $field) {
$rawLabel = $field['label'] ?? null;
$value = (string)($field['value'] ?? '');
// No label: value takes the whole row
if (!is_string($rawLabel) || trim($rawLabel) === '') {
static::writeText(
$pdf, $value,
$currentX, $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$usableWidth, $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $field_layout['rowAdvance'];
continue;
}
$labelText = rtrim($field['label'], ':') . ':';
static::writeText(
$pdf, $labelText,
$currentX, $currentY,
'freesans', '', $field_layout['labelSize'], 'L',
$field_layout['labelWidth'], $field_layout['rowAdvance'], true,
);
$currentY += $labelSize + $labelMargin;
static::writeText(
$pdf, $field['value'],
$currentX, $currentY,
'freemono', 'B', $fieldSize, 'L',
$usableWidth, $fieldSize, true, 0, 0.01
$field_layout['valueX'], $currentY,
'freemono', 'B', $field_layout['fieldSize'], 'L',
$field_layout['valueWidth'], $field_layout['rowAdvance'], true, 0, 0.01
);
$currentY += $fieldSize + $fieldMargin;
$currentY += $field_layout['rowAdvance'];;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
+3 -1
View File
@@ -47,7 +47,7 @@ class License extends Depreciable
];
protected $rules = [
'name' => 'required|string|min:3|max:255',
'name' => 'required|string|max:255',
'seats' => 'required|min:1|integer|limit_change:10000', // limit_change is a "pseudo-rule" that translates into 'between', see prepareLimitChangeRule() below
'license_email' => 'email|nullable|max:120',
'license_name' => 'string|nullable|max:100',
@@ -223,6 +223,7 @@ class License extends Depreciable
$logAction->created_by = auth()->id() ?: 1; // We don't have an id while running the importer from CLI.
$logAction->note = "deleted {$change} seats";
$logAction->target_id = null;
$logAction->quantity = $change;
$logAction->logaction('delete seats');
return true;
@@ -259,6 +260,7 @@ class License extends Depreciable
$logAction->created_by = auth()->id() ?: 1; // Importer.
$logAction->note = "added {$change} seats";
$logAction->target_id = null;
$logAction->quantity = $change;
$logAction->logaction('add seats');
}
+1 -1
View File
@@ -26,7 +26,7 @@ class Location extends SnipeModel
protected $table = 'locations';
protected $rules = [
'name' => 'required|min:2|max:255|unique_undeleted',
'name' => 'required|max:255|unique_undeleted',
'address' => 'max:191|nullable',
'address2' => 'max:191|nullable',
'city' => 'max:191|nullable',
+1 -1
View File
@@ -21,7 +21,7 @@ class Manufacturer extends SnipeModel
// Declare the rules for the form validation
protected $rules = [
'name' => 'required|min:2|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL',
'name' => 'required|max:255|unique:manufacturers,name,NULL,id,deleted_at,NULL',
'url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
'support_email' => 'email|nullable',
'support_url' => 'nullable|starts_with:http://,https://,afp://,facetime://,file://,irc://',
+1 -1
View File
@@ -25,7 +25,7 @@ class PredefinedKit extends SnipeModel
* Category validation rules
*/
public $rules = [
'name' => 'required|min:1|max:255|unique',
'name' => 'required|max:255|unique',
];
use ValidatingTrait;
+1 -1
View File
@@ -21,7 +21,7 @@ class Statuslabel extends SnipeModel
protected $hidden = ['user_id', 'deleted_at'];
protected $rules = [
'name' => 'required|string|unique_undeleted',
'name' => 'required|max:255|string|unique_undeleted',
'notes' => 'string|nullable',
'deployable' => 'required',
'pending' => 'required',

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