Compare commits

...

253 Commits

Author SHA1 Message Date
snipe
a4ba070c84 Include manufacturer_id
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 14:03:30 +01:00
snipe
2225c44b12 Check for valid depreciation_date()
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 13:49:25 +01:00
snipe
77c6392b16 Return smaller payload on asset response
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 13:49:17 +01:00
snipe
8a80d9009d Refomatted hidden array
Signed-off-by: snipe <snipe@snipe.net>
2025-07-16 12:24:48 +01:00
snipe
6f57d6b876 Merge pull request #17407 from grokability/fixes-signature-pad-chrome
Fixed display of acceptance button if signature is not required
2025-07-15 10:58:34 +01:00
snipe
e0bad99ea1 Fixes display of acceptannce button if signature is not required
Signed-off-by: snipe <snipe@snipe.net>
2025-07-15 10:55:30 +01:00
snipe
e39eb09cfb Merge pull request #17390 from Godmartinz/unhandled-redirect-error
FIXED redirect option being NULL
2025-07-10 19:41:40 +01:00
Godfrey M
465ac1d1e1 remove ternary 2025-07-10 08:39:13 -07:00
Godfrey M
18d6becebc populate other_redirect in store method 2025-07-10 08:36:15 -07:00
snipe
b0917a5131 Merge pull request #17385 from grokability/17383-fix-api-route-path
Fixed #17383 - re-add `/hardware/` as an object type in the file upload API
2025-07-10 13:10:43 +01:00
snipe
0972c4e340 Re-added /hardware/ as viable route for API file uploads
Signed-off-by: snipe <snipe@snipe.net>
2025-07-10 13:06:22 +01:00
snipe
43a237bf95 Merge pull request #17378 from grokability/phpcs/models
Code formatting fixes
2025-07-09 21:55:30 +01:00
snipe
95f867b267 Code formatting fixes
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 21:48:53 +01:00
snipe
e96daf469a Better phrasing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 21:00:54 +01:00
snipe
f2cdfe9e47 Normalize textarea for notes in acceptance form
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:58:07 +01:00
snipe
929b67e768 Merge pull request #17376 from grokability/small-tweak-to-acceptance-ui
Better indicate via submit button colors and messaging that something is about to be accepted or declined
2025-07-09 20:21:50 +01:00
snipe
0573dc136a Put the sig check back
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:16:30 +01:00
snipe
48588f6a9e Small UI sugar on the acceptance/signature screen
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 20:08:19 +01:00
snipe
88579b9bf3 Merge pull request #17374 from uberbrady/improve_inline_videos
Fixed [FD-49538 ] - use a Video tag for video files for non-Safari usage
2025-07-09 15:47:19 +01:00
snipe
e8bb9bde99 Fixed #8201 - splits first_name and last_name in user export
Signed-off-by: snipe <snipe@snipe.net>
2025-07-09 15:46:42 +01:00
Brady Wetherington
0ee3cca4da Use a Video tag for video files for non-Safari usage 2025-07-09 15:15:53 +01:00
snipe
f89ee6b7f2 Merge pull request #17361 from Godmartinz/return-custom-textarea_input
Fixed #7957 - custom field Textarea input not retaining when switching Asset Models with shared fields
2025-07-08 22:02:07 +01:00
snipe
aebfb52c85 Merge pull request #17362 from Godmartinz/license-redirect-bug
Fixed #17310 - 500 on redirect when checking in a license seat
2025-07-08 21:59:22 +01:00
Godfrey M
667bd7af0e fix checkout_to_type being null when checking in 2025-07-08 13:53:10 -07:00
snipe
bbf69bc582 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-07-08 21:26:15 +01:00
snipe
f2b7a3d002 Updated language strings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-08 21:25:31 +01:00
Godfrey M
3fd9e3ab56 include textareas input return 2025-07-08 12:02:56 -07:00
snipe
8e11466a54 Added query scope
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 22:09:23 +01:00
snipe
dade9797d5 Bumped hash
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 21:46:13 +01:00
snipe
97c1e65ffc Fixed fieldname
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 20:58:26 +01:00
snipe
b4e22f4a21 Small fix for seat listing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 20:56:26 +01:00
snipe
58b6feb3ca Merge pull request #17356 from grokability/show-only-taken-licenses
[FD-49569 ] - Show only assigned in license tab
2025-07-07 17:05:38 +01:00
snipe
41c4920d45 Show only assigned in license tab
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 16:58:36 +01:00
snipe
d1ddd8de98 Re-add column selector
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 16:23:48 +01:00
snipe
f41307eb4a Merge pull request #17353 from grokability/fixes-#14295-send-acceptance-on-signing
Fixed #14295 - allow user to receive an email PDF upon signing
2025-07-07 15:59:03 +01:00
snipe
59de77feb0 Use company name if provided instead of site name
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:55:40 +01:00
snipe
8ebbcf6e80 Removed console commands
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:55:31 +01:00
snipe
24c6e836dd Added checkbox toggle
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:51:24 +01:00
snipe
8e38b3898e Fixes #14295 - allow user to receive an email PDF upon signing
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 15:38:36 +01:00
snipe
ce9a5e35c9 Merge pull request #17352 from grokability/fixed-#17273-notes-in-upcoming-audit
Fixed #17273 -  switch to HTML table from markdown
2025-07-07 14:42:43 +01:00
snipe
b092779697 Fixed #17273 - switch to HTML table from markdown
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 14:37:38 +01:00
snipe
ab30a96d16 Merge pull request #17327 from Godmartinz/asset_model_redirect
FIXED: #15861 adds a redirect option for asset model and previous page
2025-07-07 14:00:31 +01:00
snipe
dab0fb16ad Merge pull request #17291 from Godmartinz/fix_bulk_checkout_focus
Fixes #12094 - Adds focus to select2 in bulk checkout
2025-07-07 13:57:12 +01:00
snipe
5be398bc99 Merge pull request #17350 from grokability/tighter-control-on-company
Fixes #17302 - Tighter control on company
2025-07-07 13:45:08 +01:00
snipe
fe4172957f Merge pull request #17351 from grokability/smaller-pdfs
Fixed #17349 - enable_font_subsetting in PDFs
2025-07-07 13:33:29 +01:00
snipe
ff3a59d347 Fixed #17349 - enable_font_subsetting in PDFs
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:32:25 +01:00
snipe
f9aedea26f Eager load admin
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:21:07 +01:00
snipe
5abd2c7151 Added tests
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 13:12:16 +01:00
snipe
bfcaf4f37b Removed unecessary use statement
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:36:59 +01:00
snipe
5f4e1835bc Removed unused method
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:36:16 +01:00
snipe
c1f1ae6b64 Removed logging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:31:12 +01:00
snipe
c4fcc6c24e Removed direct scoping calls
Signed-off-by: snipe <snipe@snipe.net>
2025-07-07 12:26:30 +01:00
snipe
dd73ad9941 Merge pull request #17341 from Godmartinz/query_error_rb19824
Fixed #17193: perform Orderby before Collection in Bulk Assets Controller
2025-07-07 11:19:37 +01:00
snipe
ac21f7569f Merge pull request #17346 from grokability/add-video-uploads
[FD-49538] Add video/audio uploads
2025-07-07 11:15:19 +01:00
snipe
4ef0158da4 Use preview instead of image text
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:19:29 +01:00
snipe
4db3b3ba0e Use config array for extensions in restore tool
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:19:13 +01:00
snipe
dc43d85323 Check for audio files
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:15:30 +01:00
snipe
62651f381c Expand safe allowed inline files
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:13:57 +01:00
snipe
3e9098907a Use config file for file types/mimes
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:11:31 +01:00
snipe
e18df250f8 Removed console debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-07-03 20:08:08 +01:00
Godfrey M
be5c5a51da get desired behavior of select-2 2025-07-02 11:20:04 -07:00
Godfrey M
a728fad675 perform orderBy on query before converting to a Collection 2025-07-02 11:06:35 -07:00
snipe
185629b310 Merge pull request #17338 from grokability/small-depreciation-tweaks
Fixed #1909 - Small depreciation tweaks
2025-07-02 18:37:21 +01:00
snipe
30ebea4f2d Return int count
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 18:24:27 +01:00
snipe
b135c1eac2 Updated language strings in view
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 18:15:06 +01:00
snipe
88fef73d6f Use new translation
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:54:10 +01:00
snipe
556a9039e9 New strings
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:55 +01:00
snipe
cdfe6c21c1 Tightened HTML, added translations
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:42 +01:00
snipe
b094ebdd66 Removed validation of > 0
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:53:32 +01:00
snipe
526a7ddea6 Merge pull request #17337 from grokability/fixes-#17112-ldap-location-set-to-0
Fixed #17112 - Set location ID to null instead of 0
2025-07-02 17:20:34 +01:00
snipe
bb5ad31cba Merge pull request #17336 from uberbrady/safer_deserialize
Use safer deserialization defaults
2025-07-02 17:15:48 +01:00
snipe
549da2efed Set location ID to null instead of 0
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:15:18 +01:00
snipe
e5e586dc43 Attempt to generalize companyable in company scope
Signed-off-by: snipe <snipe@snipe.net>
2025-07-02 17:12:55 +01:00
Brady Wetherington
8a682beb0e Use safer deserialization defaults 2025-07-02 14:49:12 +01:00
snipe
699e9f75c9 Fixed RB-19892
Check for location before trying to grab company property

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

Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 16:57:17 +01:00
snipe
ec65fc1e65 Move manager view option to misc section
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 16:56:56 +01:00
snipe
32d8646d96 Add @lukaskraic as a contributor 2025-06-17 16:26:17 +01:00
snipe
e8835fc2b1 Merge pull request #17096 from lukaskraic/feature/manager-view-v2
Manager View Feature
2025-06-17 16:24:40 +01:00
snipe
054a06c5dc Update resources/lang/en-US/admin/settings/general.php
Co-authored-by: Marcus Moore <contact@marcusmoore.io>
2025-06-17 15:39:26 +01:00
snipe
9c61d2eb22 Removed common elements in tables
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 14:25:02 +01:00
snipe
d66b6cfee6 Merge pull request #17220 from grokability/added-advanced-search-to-activity-log
Added advanced search to activity log
2025-06-17 13:24:31 +01:00
snipe
89c0427b2f Added fullscreen option
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:24:02 +01:00
snipe
3fec10d447 Added advanced search to activity log
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:20:39 +01:00
snipe
f8b4981bfe Merge pull request #17219 from grokability/fixes-#17213-search-by-admin
Fixes #17213:  search by admin (created_by) in activity report
2025-06-17 13:02:51 +01:00
snipe
130669a2f9 Added adminuser relationship in Searchable to concat first name and last name
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 13:01:57 +01:00
snipe
c2c79ee231 Make created_by searchable
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:56:39 +01:00
snipe
86f10bd702 Added employee number to searchable relation
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:56:30 +01:00
snipe
b496a06fc0 Merge pull request #17218 from grokability/fixed-#17212-hide-columns-on-print
Fixed #17212 - hide columns from print view
2025-06-17 12:37:43 +01:00
snipe
f865a6cb37 Added 'printIgnore' => true, to presenters to hide checkbox and action column from print view
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 12:35:05 +01:00
snipe
89186ea4f8 Merge pull request #17195 from grokability/#17192-fixes-wonky-layout-on-bulk-edit
Fixed #17192 - wonky layout on bulk edit screens
2025-06-17 11:55:31 +01:00
snipe
fb19985186 Use the same variable for fieldset closing tag
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 11:52:47 +01:00
snipe
ebc6e1221a Merge pull request #17198 from akemidx/ghissue-16241
Fixed #16241 - menu items not getting class="active" on click.
2025-06-17 11:40:56 +01:00
snipe
2b91dcb700 Merge pull request #17216 from grokability/#17215-wrong-logo-check-in-print
Fixed #17215: Check for acceptance logo vs regular logo
2025-06-17 11:38:19 +01:00
snipe
9d2e333fd6 Check for acceptance logo vs regular logo
Signed-off-by: snipe <snipe@snipe.net>
2025-06-17 11:36:10 +01:00
Godfrey M
013ad1069c saving certs sooner in the stack 2025-06-16 16:30:00 -07:00
Godfrey M
ec059717f6 adds id to XML textbox, now updates 2025-06-16 16:01:11 -07:00
akemidx
418566db3f fixes the easy ones 2025-06-16 18:41:36 -04:00
snipe
7be9463be6 Merge pull request #17180 from grokability/#12653-added-jobtitle-to-asset-listing
Fixed #12653 - added jobtitle to asset listing
2025-06-16 20:29:01 +01:00
snipe
51712bc7d6 Check for whether any of the models have a fieldset
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 16:40:07 +01:00
snipe
7b889d22d2 Fixed HTML
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 16:14:01 +01:00
snipe
e8aad989ec Updated translations
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 14:27:47 +01:00
snipe
4006d64d60 Bumped version
Signed-off-by: snipe <snipe@snipe.net>
2025-06-16 14:25:59 +01:00
snipe
d792d99375 Removed stray space for favicon
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 03:51:50 +01:00
snipe
b11036a2e5 Fixed #11977 - changed path in backups language
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 03:40:54 +01:00
snipe
01de69a250 Merge pull request #17181 from grokability/fixed-#14542-missing-fullscreen-in-locations
Fixed #14542 - added fullscreen option for location view tabs
2025-06-15 02:57:51 +01:00
snipe
b842aa11e5 Remove debugging
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 02:25:25 +01:00
snipe
ff01078b60 Fixed #12653 - adds job title to asset listing
Signed-off-by: snipe <snipe@snipe.net>
2025-06-15 02:23:53 +01:00
Marcus Moore
7f35498919 Remove some test cases 2025-06-12 15:56:24 -07:00
Marcus Moore
297205ff91 Tests 2025-06-12 15:55:25 -07:00
Marcus Moore
6f99381d13 Handle accessories 2025-06-12 15:49:38 -07:00
Marcus Moore
45a42b00ad Add test 2025-06-12 15:48:48 -07:00
Marcus Moore
6586858284 Scaffold some test cases 2025-06-12 15:31:32 -07:00
Marcus Moore
19cce15e54 Revert "Method signature change"
This reverts commit a60ffc0702.
2025-06-12 13:54:53 -07:00
Marcus Moore
62b8e4c46f Revert "Inline some values"
This reverts commit 74b7d27408.
2025-06-12 13:54:46 -07:00
Marcus Moore
74b7d27408 Inline some values 2025-06-12 13:49:19 -07:00
Marcus Moore
a60ffc0702 Method signature change 2025-06-12 13:46:05 -07:00
Marcus Moore
32526d77b8 Add todo 2025-06-12 13:35:22 -07:00
Marcus Moore
5711a9e148 Fix test 2025-06-12 13:02:26 -07:00
Marcus Moore
a0f40c2dfb Tests 2025-06-12 12:56:24 -07:00
Marcus Moore
34636016eb Improve test 2025-06-12 12:47:03 -07:00
Marcus Moore
95027e329c Formatting 2025-06-12 12:44:29 -07:00
Marcus Moore
c9778a73c7 Wire up category controller 2025-06-12 12:32:34 -07:00
Marcus Moore
628d2a0a0a Flesh out mail contents 2025-06-12 12:12:38 -07:00
Marcus Moore
cc0ff1ec1f Handle missing recipient 2025-06-12 11:53:30 -07:00
Marcus Moore
cd53fc6318 Scaffold email contents 2025-06-11 16:56:40 -07:00
Marcus Moore
ae98f6276e Replace reject with declined 2025-06-05 17:24:57 -07:00
Marcus Moore
7424a5987b Add subject 2025-06-05 17:22:31 -07:00
Marcus Moore
2d08749207 Improve readability 2025-06-05 16:56:11 -07:00
Marcus Moore
db50e98ae3 Populate tests 2025-06-05 16:51:44 -07:00
Marcus Moore
7ec0925c69 Scaffold out tests 2025-06-05 16:44:44 -07:00
Marcus Moore
df1361aa43 Scaffold test 2025-06-05 16:42:31 -07:00
akemidx
6db04c86df making fields active. first pass 2025-06-05 18:46:33 -04:00
Marcus Moore
bec80b443c WIP: begin to send email 2025-06-05 15:15:22 -07:00
Marcus Moore
333501fe55 WIP: create mail class 2025-06-05 15:07:19 -07:00
Marcus Moore
063553d4f7 Scaffold scenarios 2025-06-05 14:57:15 -07:00
Marcus Moore
19b9e50281 Update livewire component for alert_on_response 2025-06-05 14:40:19 -07:00
Marcus Moore
2a68b4aeff Update test for updating category with alert_on_response 2025-06-05 14:36:06 -07:00
Marcus Moore
5e25150521 Add another test case 2025-06-05 14:26:56 -07:00
Marcus Moore
cb183d3645 Store alert_on_response_id on CheckoutAcceptance 2025-06-05 14:06:22 -07:00
Marcus Moore
96bce301a0 Add alert_on_response to Category 2025-06-05 13:43:20 -07:00
Marcus Moore
360f5b7538 Add alert_on_response_id to CheckoutAcceptance 2025-06-05 13:10:18 -07:00
Lukas Kraic
16fb1018a2 List users code refactoring 2025-06-05 20:05:38 +02:00
Lukas Kraic
ad6fe855a9 Fix code style: Change comments 2025-06-05 18:53:36 +02:00
Lukas Kraic
c50c97d149 Fix code style: Change comments 2025-06-05 18:18:07 +02:00
Lukas Kraic
8b98ae15f0 Fix code style: Add comment 2025-06-05 18:08:34 +02:00
Lukas Kraic
261f84d5f5 Fix code style: Remove comment 2025-06-05 17:55:46 +02:00
Lukas Kraic
29989ac24e Fix code style: Remove empty line after end comment 2025-06-05 13:50:57 +02:00
Lukas Kraic
7494fa6bc9 Fix code style: Remove unnecessary end comment from short conditional block 2025-06-05 12:03:42 +02:00
Lukas Kraic
62e50dbe52 Fix code style: Add semantically correct end comments 2025-06-05 11:56:13 +02:00
Lukas Kraic
30c090ba2d Fix code style: Add descriptive end comment for conditional block 2025-06-05 11:45:05 +02:00
Lukas Kraic
7ff82e6043 Delete comments 2025-06-05 11:07:42 +02:00
Lukas Kraic
61d3e2fb49 Fix code style: Add required whitespace in end block comments 2025-06-05 09:23:58 +02:00
Lukas Kraic
fb18c1a0be Fix code style: Remove whitespace in end block comments 2025-06-05 08:59:07 +02:00
Lukas Kraic
ea447365fa Fix Codacy warnings II 2025-06-04 15:40:17 +02:00
Lukas Kraic
60989d6766 Fix Codacy warnings 2025-06-04 15:17:55 +02:00
Lukas Kraic
84ec5aea26 Manager View Feature 2025-06-04 09:07:26 +02:00
Marcus Moore
444c13c6ea Scaffold template 2025-06-03 17:10:15 -07:00
Chris Olin
248a05a916 adds support for continuous 53mm and 0.59in printers 2025-05-06 11:26:52 -04:00
Chris Olin
2c141579dd adds support for GenericTape label printers, includes class for 53mm tape printer 2025-05-06 11:26:40 -04:00
1197 changed files with 24712 additions and 5260 deletions

View File

@@ -4180,6 +4180,15 @@
"contributions": [
"code"
]
},
{
"login": "lukaskraic",
"name": "Lukas Kraic",
"avatar_url": "https://avatars.githubusercontent.com/u/58696401?v=4",
"profile": "https://github.com/lukaskraic",
"contributions": [
"code"
]
}
]
}

View File

@@ -67,7 +67,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
| [<img src="https://avatars.githubusercontent.com/u/80526133?v=4" width="110px;"/><br /><sub>AlexanderWPapyrus</sub>](https://github.com/AlexanderWPapyrus)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AlexanderWPapyrus "Code") | [<img src="https://avatars.githubusercontent.com/u/306231?v=4" width="110px;"/><br /><sub>Alexandr Hacicheant</sub>](https://github.com/disc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=disc "Code") | [<img src="https://avatars.githubusercontent.com/u/3032891?v=4" width="110px;"/><br /><sub>Hex</sub>](https://hex128.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=hex128 "Code") | [<img src="https://avatars.githubusercontent.com/u/8697942?v=4" width="110px;"/><br /><sub>Arunas Skirius</sub>](https://github.com/arukompas)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arukompas "Code") | [<img src="https://avatars.githubusercontent.com/u/104396?v=4" width="110px;"/><br /><sub>Ben Periton</sub>](https://github.com/benperiton)<br />[💻](https://github.com/snipe/snipe-it/commits?author=benperiton "Code") | [<img src="https://avatars.githubusercontent.com/u/11906832?v=4" width="110px;"/><br /><sub>Byron Wolfman</sub>](https://wolfman.dev/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=byronwolfman "Code") | [<img src="https://avatars.githubusercontent.com/u/56485508?v=4" width="110px;"/><br /><sub>Calvin</sub>](https://github.com/CalvinSchwartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=CalvinSchwartz "Code") |
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") |
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

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

View File

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

View File

@@ -329,9 +329,9 @@ class RestoreFromBackup extends Command
}
}
}
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico', 'avif'
];
$good_extensions = config('filesystems.allowed_upload_extensions_array');
foreach (array_merge($private_files, $public_files) as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {

View File

@@ -116,9 +116,17 @@ class Handler extends ExceptionHandler
return response()->json(Helper::formatStandardApiResponse('error', null, 'Method not allowed'), 405);
default:
return response()->json(Helper::formatStandardApiResponse('error', null, $statusCode), $statusCode);
}
}
// This handles API validation exceptions that happen at the Form Request level, so they
// never even get to the controller where we normally nicely format JSON responses
if ($e instanceof ValidationException) {
$response = $this->invalidJson($request, $e);
return response()->json(Helper::formatStandardApiResponse('error', null, $e->errors()), 200);
}
}

View File

@@ -13,6 +13,7 @@ use App\Models\Setting;
use App\Models\Statuslabel;
use App\Models\License;
use App\Models\Location;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
use Carbon\Carbon;
@@ -876,6 +877,48 @@ class Helper
return false;
}
/**
* Check if the file is a video, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [B. Wetherington] [<bwetherington@grokability.com>]
* @since [v8.1.18]
*/
public static function checkUploadIsVideo($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'video/mp4') || ($filetype == 'video/quicktime') || ($filetype == 'video/mpeg') || ($filetype == 'video/ogg') || ($filetype == 'video/webm') || ($filetype == 'video/x-msvide')) {
return $filetype;
}
return false;
}
/**
* Check if the file is audio, so we can show a preview
*
* @param File $file
* @return string | Boolean
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*/
public static function checkUploadIsAudio($file)
{
$finfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$filetype = @finfo_file($finfo, $file);
finfo_close($finfo);
if (($filetype == 'audio/mpeg') || ($filetype == 'audio/ogg')) {
return $filetype;
}
return false;
}
/**
* Walks through the permissions in the permissions config file and determines if
* permissions are granted based on a $selected_arr array.
@@ -1170,6 +1213,15 @@ class Helper
// Misc
'pdf' => 'far fa-file-pdf',
'lic' => 'far fa-save',
// video
'mov' => 'fa-solid fa-video',
'mp4' => 'fa-solid fa-video',
// audio
'ogg' => 'fa-solid fa-file-audio',
'mp3' => 'fa-solid fa-file-audio',
'wav' => 'fa-solid fa-file-audio',
];
if ($extension && array_key_exists($extension, $allowedExtensionMap)) {
@@ -1480,68 +1532,64 @@ class Helper
}
static public function getRedirectOption($request, $id, $table, $item_id = null)
static public function getRedirectOption($request, $id, $table, $item_id = null) : RedirectResponse
{
$redirect_option = Session::get('redirect_option');
$checkout_to_type = Session::get('checkout_to_type');
$redirect_option = Session::get('redirect_option') ?? $request->redirect_option;
$checkout_to_type = Session::get('checkout_to_type') ?? null;
$checkedInFrom = Session::get('checkedInFrom');
$other_redirect = Session::get('other_redirect');
$backUrl = Session::pull('back_url', route('home'));
// return to previous page
if ($redirect_option === 'back') {
if ($backUrl === route('home')) {
return redirect()->to($backUrl)
->with('warning', trans('general.page_error'));
}
return redirect()->to($backUrl);
}
// return to index
if ($redirect_option == 'index') {
switch ($table) {
case "Assets":
return route('hardware.index');
case "Users":
return route('users.index');
case "Licenses":
return route('licenses.index');
case "Accessories":
return route('accessories.index');
case "Components":
return route('components.index');
case "Consumables":
return route('consumables.index');
}
return match ($table) {
'Assets' => redirect()->route('hardware.index'),
'Users' => redirect()->route('users.index'),
'Licenses' => redirect()->route('licenses.index'),
'Accessories' => redirect()->route('accessories.index'),
'Components' => redirect()->route('components.index'),
'Consumables' => redirect()->route('consumables.index'),
};
}
// return to thing being assigned
if ($redirect_option == 'item') {
switch ($table) {
case "Assets":
return route('hardware.show', $id ?? $item_id);
case "Users":
return route('users.show', $id ?? $item_id);
case "Licenses":
return route('licenses.show', $id ?? $item_id);
case "Accessories":
return route('accessories.show', $id ?? $item_id);
case "Components":
return route('components.show', $id ?? $item_id);
case "Consumables":
return route('consumables.show', $id ?? $item_id);
}
return match ($table) {
'Assets' => redirect()->route('hardware.show', $id ?? $item_id),
'Users' => redirect()->route('users.show', $id ?? $item_id),
'Licenses' => redirect()->route('licenses.show', $id ?? $item_id),
'Accessories' => redirect()->route('accessories.show', $id ?? $item_id),
'Components' => redirect()->route('components.show', $id ?? $item_id),
'Consumables' => redirect()->route('consumables.show', $id ?? $item_id),
};
}
// return to assignment target
if ($redirect_option == 'target') {
switch ($checkout_to_type) {
case 'user':
return route('users.show', $request->assigned_user ?? $checkedInFrom);
case 'location':
return route('locations.show', $request->assigned_location ?? $checkedInFrom);
case 'asset':
return route('hardware.show', $request->assigned_asset ?? $checkedInFrom);
}
return match ($checkout_to_type) {
'user' => redirect()->route('users.show', $request->assigned_user ?? $checkedInFrom),
'location' => redirect()->route('locations.show', $request->assigned_location ?? $checkedInFrom),
'asset' => redirect()->route('hardware.show', $request->assigned_asset ?? $checkedInFrom),
};
}
// return to somewhere else
if ($redirect_option == 'other_redirect') {
switch ($other_redirect) {
case 'audit':
return route('assets.audit.due');
}
return match ($other_redirect) {
'audit' => redirect()->route('assets.audit.due'),
'model' => redirect()->route('models.show', $request->model_id),
};
}

View File

@@ -37,17 +37,25 @@ class StorageHelper
* @param $file_with_path
* @return bool
*/
public static function allowSafeInline($file_with_path) {
public static function allowSafeInline($file_with_path)
{
$allowed_inline = [
'pdf',
'svg',
'jpg',
'gif',
'svg',
'avif',
'webp',
'gif',
'gif',
'jpg',
'mov',
'mp3',
'mp4',
'ogg',
'pdf',
'png',
'svg',
'svg',
'wav',
'webm',
'webp',
];
@@ -59,10 +67,24 @@ class StorageHelper
}
public static function getFiletype($file_with_path)
{
// The file exists and is allowed to be displayed inline
if (Storage::exists($file_with_path)) {
return pathinfo($file_with_path, PATHINFO_EXTENSION);
}
return null;
}
/**
* Decide whether to show the file inline or download it.
*/
public static function showOrDownloadFile($file, $filename) {
public static function showOrDownloadFile($file, $filename)
{
$headers = [];

View File

@@ -83,7 +83,8 @@ class AccessoriesController extends Controller
// Was the accessory created?
if ($accessory->save()) {
// Redirect to the new accessory page
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.create.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.create.success'));
}
return redirect()->back()->withInput()->withErrors($accessory->getErrors());
@@ -167,7 +168,8 @@ class AccessoriesController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
if ($accessory->save()) {
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.update.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.update.success'));
}
} else {
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));

View File

@@ -78,7 +78,8 @@ class AccessoryCheckinController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option')]);
return redirect()->to(Helper::getRedirectOption($request, $accessory->id, 'Accessories'))->with('success', trans('admin/accessories/message.checkin.success'));
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
->with('success', trans('admin/accessories/message.checkin.success'));
}
// Redirect to the accessory management page with error
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.checkin.error'));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -114,6 +114,7 @@ class AssetsController extends Controller
'byod',
'asset_eol_date',
'requestable',
'jobtitle',
];
$filter = [];
@@ -395,6 +396,9 @@ class AssetsController extends Controller
case 'assigned_to':
$assets->OrderAssigned($order);
break;
case 'jobtitle':
$assets->OrderByJobTitle($order);
break;
case 'created_by':
$assets->OrderByCreatedByName($order);
break;

View File

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

View File

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

View File

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

View File

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

View File

@@ -677,7 +677,6 @@ class UsersController extends Controller
$this->authorize('view', License::class);
if ($user = User::where('id', $id)->withTrashed()->first()) {
$this->authorize('update', $user);
$licenses = $user->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
@@ -748,7 +747,7 @@ class UsersController extends Controller
*/
public function eulas(User $user, ActionlogsTransformer $transformer)
{
$this->authorize('view', Asset::class);
$this->authorize('view', User::class);
$eulas = $user->eulas;
return response()->json(

View File

@@ -153,7 +153,8 @@ class AssetCheckinController extends Controller
if ($asset->save()) {
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.checkin.success'));
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkin.success'));
}
// Redirect to the asset management page with error
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.checkin.error').$asset->getErrors());

View File

@@ -123,7 +123,7 @@ class AssetCheckoutController extends Controller
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->checkOut($target, $admin, $checkout_at, $expected_checkin, $request->get('note'), $request->get('name'))) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.checkout.success'));
}
// Redirect to the asset management page with error

View File

@@ -227,24 +227,27 @@ class AssetsController extends Controller
}
}
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
session()->put(['redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => 'model' ]);
if ($successes) {
if ($failures) {
//some succeeded, some failed
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested
return Helper::getRedirectOption($request, $asset->id, 'Assets') //FIXME - not tested
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]))
->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)]));
} else {
if (count($successes) == 1) {
//the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed
//and re-translated
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', $asset), 'id', 'tag' => e($asset->asset_tag)]));
} else {
//multi-success
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)]));
}
}
@@ -265,6 +268,7 @@ class AssetsController extends Controller
public function edit(Asset $asset) : View | RedirectResponse
{
$this->authorize($asset);
session()->put('back_url', url()->previous());
return view('hardware/edit')
->with('item', $asset)
->with('statuslabel_list', Helper::statusLabelList())
@@ -427,11 +431,15 @@ class AssetsController extends Controller
}
}
}
session()->put([
'redirect_option' => $request->get('redirect_option'),
'checkout_to_type' => $request->get('checkout_to_type'),
'other_redirect' => $request->get('redirect_option') === 'other_redirect' ? 'model' : null,
]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
if ($asset->save()) {
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))
return Helper::getRedirectOption($request, $asset->id, 'Assets')
->with('success', trans('admin/hardware/message.update.success'));
}
@@ -996,7 +1004,7 @@ class AssetsController extends Controller
}
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets'))->with('success', trans('admin/hardware/message.audit.success'));
return Helper::getRedirectOption($request, $asset->id, 'Assets')->with('success', trans('admin/hardware/message.audit.success'));
}
return redirect()->back()->withInput()->withErrors($asset->getErrors());

View File

@@ -112,11 +112,47 @@ class BulkAssetsController extends Controller
// This handles all of the pivot sorting below (versus the assets.* fields in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.id';
$assets = Asset::with('assignedTo', 'location', 'model')
$query = Asset::with('assignedTo', 'location', 'model')
->whereIn('assets.id', $asset_ids)
->withTrashed();
$assets = $assets->get();
switch ($sort_override) {
case 'model':
$query->OrderModels($order);
break;
case 'model_number':
$query->OrderModelNumber($order);
break;
case 'category':
$query->OrderCategory($order);
break;
case 'manufacturer':
$query->OrderManufacturer($order);
break;
case 'company':
$query->OrderCompany($order);
break;
case 'location':
$query->OrderLocation($order);
break;
case 'rtd_location':
$query->OrderRtdLocation($order);
break;
case 'status_label':
$query->OrderStatus($order);
break;
case 'supplier':
$query->OrderSupplier($order);
break;
case 'assigned_to':
$query->OrderAssigned($order);
break;
default:
$query->orderBy($column_sort, $order);
break;
}
$assets = $query->get();
if ($assets->isEmpty()) {
Log::debug('No assets were found for the provided IDs', ['ids' => $asset_ids]);
@@ -169,40 +205,7 @@ class BulkAssetsController extends Controller
}
}
switch ($sort_override) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
case 'company':
$assets->OrderCompany($order);
break;
case 'location':
$assets->OrderLocation($order);
case 'rtd_location':
$assets->OrderRtdLocation($order);
break;
case 'status_label':
$assets->OrderStatus($order);
break;
case 'supplier':
$assets->OrderSupplier($order);
break;
case 'assigned_to':
$assets->OrderAssigned($order);
break;
default:
$assets->orderBy($column_sort, $order);
break;
}
return redirect()->back()->with('error', 'No action selected');
}
@@ -281,6 +284,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('null_expected_checkin_date'))
|| ($request->filled('null_next_audit_date'))
|| ($request->filled('null_asset_eol_date'))
|| ($request->filled('null_notes'))
|| ($request->anyFilled($custom_field_columns))
|| ($request->anyFilled(array_keys($null_custom_fields_inputs)))
@@ -305,7 +309,8 @@ class BulkAssetsController extends Controller
->conditionallyAddItem('supplier_id')
->conditionallyAddItem('warranty_months')
->conditionallyAddItem('next_audit_date')
->conditionallyAddItem('asset_eol_date');
->conditionallyAddItem('asset_eol_date')
->conditionallyAddItem('notes');
foreach ($custom_field_columns as $key => $custom_field_column) {
$this->conditionallyAddItem($custom_field_column);
}
@@ -362,6 +367,10 @@ class BulkAssetsController extends Controller
}
}
if ($request->input('null_notes')=='1') {
$this->update_array['notes'] = null;
}
if ($request->filled('purchase_cost')) {

View File

@@ -68,6 +68,7 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');
$category->created_by = auth()->id();
@@ -121,6 +122,7 @@ class CategoriesController extends Controller
$category->eula_text = $request->input('eula_text');
$category->use_default_eula = $request->input('use_default_eula', '0');
$category->require_acceptance = $request->input('require_acceptance', '0');
$category->alert_on_response = $request->input('alert_on_response', '0');
$category->checkin_email = $request->input('checkin_email', '0');
$category->notes = $request->input('notes');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,15 +3,21 @@
namespace App\Http\Controllers;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\ProfileTransformer;
use App\Models\Actionlog;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\CurrentInventory;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* This controller handles all actions related to User Profiles for
* the Snipe-IT Asset Management application.
@@ -220,7 +226,7 @@ class ProfileController extends Controller
if (!$user = User::find(auth()->id())) {
return redirect()->back()
->with('error', trans('admin/users/message.user_not_found', ['id' => $id]));
->with('error', trans('admin/users/message.user_not_found', ['id' => auth()->id()]));
}
if (empty($user->email)) {
return redirect()->back()->with('error', trans('admin/users/message.user_has_no_email'));
@@ -234,4 +240,28 @@ class ProfileController extends Controller
return redirect()->back()->with('success', trans('admin/users/general.user_notified'));
}
public function getStoredEula($filename) : Response | BinaryFileResponse | RedirectResponse
{
$logentry = Actionlog::where('filename', $filename)->first();
// Make sure the user has permission to view this file
if (auth()->id() != $logentry->target_id) {
return redirect()->route('account')->with('error', trans('general.generic_model_not_found', ['model' => 'file']));
}
if (config('filesystems.default') == 's3_private') {
return redirect()->away(Storage::disk('s3_private')->temporaryUrl('private_uploads/eula-pdfs/'.$filename, now()->addMinutes(5)));
}
if (Storage::exists('private_uploads/eula-pdfs/'.$filename)) {
return response()->download(config('app.private_uploads').'/eula-pdfs/'.$filename);
}
return redirect()->back()->with('error', trans('general.file_does_not_exist'));
}
}

View File

@@ -352,6 +352,7 @@ class SettingsController extends Controller
$setting->dash_chart_type = $request->input('dash_chart_type');
$setting->profile_edit = $request->input('profile_edit', 0);
$setting->require_checkinout_notes = $request->input('require_checkinout_notes', 0);
$setting->manager_view_enabled = $request->input('manager_view_enabled', 0);
if ($request->input('per_page') != '') {

View File

@@ -154,7 +154,8 @@ class UsersController extends Controller
$user->notify(new WelcomeNotification($data));
}
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))->with('success', trans('admin/users/message.success.create'));
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.create'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -186,6 +187,7 @@ class UsersController extends Controller
{
$this->authorize('update', User::class);
session()->put('back_url', url()->previous());
$user = User::with(['assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc'])->withTrashed()->find($user->id);
if ($user) {
@@ -312,7 +314,7 @@ class UsersController extends Controller
if ($user->save()) {
// Redirect to the user page
return redirect()->to(Helper::getRedirectOption($request, $user->id, 'Users'))
return Helper::getRedirectOption($request, $user->id, 'Users')
->with('success', trans('admin/users/message.success.update'));
}
return redirect()->back()->withInput()->withErrors($user->getErrors());
@@ -510,6 +512,8 @@ class UsersController extends Controller
trans('admin/companies/table.title'),
trans('admin/users/table.title'),
trans('general.employee_number'),
trans('admin/users/table.first_name'),
trans('admin/users/table.last_name'),
trans('admin/users/table.name'),
trans('admin/users/table.username'),
trans('admin/users/table.email'),
@@ -555,6 +559,8 @@ class UsersController extends Controller
($user->company) ? $user->company->name : '',
$user->jobtitle,
$user->employee_num,
$user->first_name,
$user->last_name,
$user->present()->fullName(),
$user->username,
$user->email,

View File

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

View File

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

View File

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

View File

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

View File

@@ -40,7 +40,6 @@ class AssetsTransformer
] : null,
'byod' => ($asset->byod ? true : false),
'requestable' => ($asset->requestable ? true : false),
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true) . ' months' : null,
'asset_eol_date' => ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date') : null,
@@ -80,6 +79,7 @@ class AssetsTransformer
'qr' => ($setting->qr_code=='1') ? config('app.url').'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png' : null,
'alt_barcode' => ($setting->alt_barcode_enabled=='1') ? config('app.url').'/uploads/barcodes/'.str_slug($setting->alt_barcode).'-'.str_slug($asset->asset_tag).'.png' : null,
'assigned_to' => $this->transformAssignedTo($asset),
'jobtitle' => $asset->assigned ? e($asset->assigned->jobtitle) : null,
'warranty_months' => ($asset->warranty_months > 0) ? e($asset->warranty_months.' '.trans('admin/hardware/form.months')) : null,
'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
'created_by' => ($asset->adminuser) ? [
@@ -101,7 +101,7 @@ class AssetsTransformer
'checkout_counter' => (int) $asset->checkout_counter,
'requests_counter' => (int) $asset->requests_counter,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
'book_value' => Helper::formatCurrencyOutput($asset->getLinearDepreciatedValue()),
'book_value' => Helper::formatCurrencyOutput($asset->getDepreciatedValue()),
];

View File

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

View File

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

View File

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

View File

@@ -3,10 +3,10 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Helpers\StorageHelper;
use App\Models\Actionlog;
use App\Models\Asset;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
class UploadedFilesTransformer
@@ -26,23 +26,26 @@ class UploadedFilesTransformer
{
$snipeModel = $file->item_type;
// This will be used later as we extend out this transformer to handle more types of uploads
if ($file->item_type == Asset::class) {
$file_url = route('show/assetfile', [$file->item_id, $file->id]);
}
$array = [
'id' => (int) $file->id,
'icon' => Helper::filetype_icon($file->filename),
'name' => e($file->filename),
'item' => ($file->item_type) ? [
'id' => (int) $file->item_id,
'type' => strtolower(class_basename($file->item_type)),
] : null,
'filename' => e($file->filename),
'url' => $file_url,
'filetype' => StorageHelper::getFiletype($file->uploads_file_path()),
'url' => $file->uploads_file_url(),
'note' => ($file->note) ? e($file->note) : null,
'created_by' => ($file->adminuser) ? [
'id' => (int) $file->adminuser->id,
'name'=> e($file->adminuser->present()->fullName),
] : null,
'created_at' => Helper::getFormattedDateObject($file->created_at, 'datetime'),
'updated_at' => Helper::getFormattedDateObject($file->updated_at, 'datetime'),
'deleted_at' => Helper::getFormattedDateObject($file->deleted_at, 'datetime'),
'inline' => StorageHelper::allowSafeInline($file->uploads_file_path()),
'exists_on_disk' => (Storage::exists($file->uploads_file_path()) ? true : false),
];
$permissions_array['available_actions'] = [
@@ -53,4 +56,5 @@ class UploadedFilesTransformer
return $array;
}
}

View File

@@ -121,7 +121,8 @@ class UsersTransformer
*/
public function transformUserCompact(User $user) : array
{
$array = [];
if (Gate::allows('view', $user) && ($user->deleted_at == '')) {
$array = [
'id' => (int) $user->id,
'image' => e($user->present()->gravatar) ?? null,
@@ -137,6 +138,7 @@ class UsersTransformer
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
}
return $array;
}

View File

@@ -88,6 +88,7 @@ abstract class Importer
'department' => 'department',
'manager_name' => 'manager full name',
'manager_username' => 'manager username',
'manager_employee_num' => 'manager employee number',
'min_amt' => 'minimum quantity',
'remote' => 'remote',
'vip' => 'vip',

View File

@@ -353,16 +353,27 @@ class ItemImporter extends Importer
* @param $user_manager string
* @return int id of company created/found
*/
public function fetchManager($user_manager_first_name, $user_manager_last_name)
public function fetchManager($user_manager_username = null, $user_manager_employee_num = null, $user_manager_first_name = null, $user_manager_last_name = null)
{
if ($user_manager_username!='') {
$manager = User::where('username', '=', $user_manager_username)->first();
$this->log('Checking on username '.$user_manager_username);
} elseif ($user_manager_employee_num!='') {
$manager = User::where('employee_num', '=', $user_manager_employee_num)->first();
$this->log('Checking on employee_num '.$user_manager_employee_num);
} else {
$manager = User::where('first_name', '=', $user_manager_first_name)
->where('last_name', '=', $user_manager_last_name)->first();
$this->log('Checking on full name');
}
if ($manager) {
$this->log('A matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' already exists');
return $manager->id;
}
$this->log('No matching Manager '.$user_manager_first_name.' '.$user_manager_last_name.' found. If their user account is being created through this import, you should re-process this file again. ');
$this->log('No matching Manager found. If their user account is being created through this import, you should re-process this file again. ');
return null;
}

View File

@@ -62,7 +62,7 @@ class UserImporter extends ItemImporter
$this->item['activated'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'activated'))) == 1) ? '1' : 0;
$this->item['employee_num'] = trim($this->findCsvMatch($row, 'employee_num'));
$this->item['department_id'] = trim($this->createOrFetchDepartment(trim($this->findCsvMatch($row, 'department'))));
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
$this->item['manager_id'] = $this->fetchManager(trim($this->findCsvMatch($row, 'manager_username')), trim($this->findCsvMatch($row, 'manager_employee_num')), trim($this->findCsvMatch($row, 'manager_first_name')), trim($this->findCsvMatch($row, 'manager_last_name')));
$this->item['remote'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'remote'))) == 1 ) ? '1' : 0;
$this->item['vip'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'vip'))) ==1 ) ? '1' : 0;
$this->item['autoassign_licenses'] = ($this->fetchHumanBoolean(trim($this->findCsvMatch($row, 'autoassign_licenses'))) ==1 ) ? '1' : 0;

View File

@@ -12,6 +12,7 @@ use App\Mail\CheckoutConsumableMail;
use App\Mail\CheckoutLicenseMail;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Category;
use App\Models\CheckoutAcceptance;
use App\Models\Component;
use App\Models\Consumable;
@@ -227,6 +228,7 @@ class CheckoutableListener
if ($checkedOutToType != "App\Models\User") {
return null;
}
if (!$event->checkoutable->requireAcceptance()) {
return null;
}
@@ -234,6 +236,13 @@ class CheckoutableListener
$acceptance = new CheckoutAcceptance;
$acceptance->checkoutable()->associate($event->checkoutable);
$acceptance->assignedTo()->associate($event->checkedOutTo);
$category = $this->getCategoryFromCheckoutable($event->checkoutable);
if ($category?->alert_on_response) {
$acceptance->alert_on_response_id = auth()->id();
}
$acceptance->save();
return $acceptance;
@@ -360,23 +369,6 @@ class CheckoutableListener
return in_array(get_class($checkoutable), $this->skipNotificationsFor);
}
private function shouldSendEmailNotifications(Model $checkoutable): bool
{
//runs a check if the category wants to send checkin/checkout emails to users
$category = match (true) {
$checkoutable instanceof Asset => $checkoutable->model->category,
$checkoutable instanceof Accessory,
$checkoutable instanceof Consumable => $checkoutable->category,
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
default => null,
};
if (!$category?->checkin_email) {
return false;
}
return true;
}
private function shouldSendWebhookNotification(): bool
{
return Setting::getSettings() && Setting::getSettings()->webhook_endpoint;
@@ -471,4 +463,14 @@ class CheckoutableListener
return array($to, $cc);
}
private function getCategoryFromCheckoutable(Model $checkoutable): ?Category
{
return match (true) {
$checkoutable instanceof Asset => $checkoutable->model->category,
$checkoutable instanceof Accessory,
$checkoutable instanceof Consumable => $checkoutable->category,
$checkoutable instanceof LicenseSeat => $checkoutable->license->category,
};
}
}

View File

@@ -6,6 +6,8 @@ use Livewire\Component;
class CategoryEditForm extends Component
{
public bool $alertOnResponse;
public $defaultEulaText;
public $eulaText;

View File

@@ -330,6 +330,8 @@ class Importer extends Component
'location' => trans('general.location'),
'manager_first_name' => trans('general.importer.manager_first_name'),
'manager_last_name' => trans('general.importer.manager_last_name'),
'manager_employee_num' => trans('general.importer.manager_employee_num'),
'manager_username' => trans('general.importer.manager_username'),
'notes' => trans('general.notes'),
'phone_number' => trans('admin/users/table.phone'),
'remote' => trans('admin/users/general.remote'),

View File

@@ -10,15 +10,16 @@ class LocationScopeCheck extends Component
{
public $mismatched = [];
public $setting;
public $is_tested = false;
public function check_locations()
{
$this->mismatched = Helper::test_locations_fmcs(false);
$this->is_tested = true;
}
public function mount() {
$this->setting = Setting::getSettings();
$this->mismatched = Helper::test_locations_fmcs(false);
}
public function render()

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Mail;
use App\Models\CheckoutAcceptance;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CheckoutAcceptanceResponseMail extends Mailable
{
use Queueable, SerializesModels;
public CheckoutAcceptance $acceptance;
public User $recipient;
public bool $wasAccepted;
/**
* Create a new message instance.
*/
public function __construct(CheckoutAcceptance $acceptance, User $recipient, bool $wasAccepted)
{
$this->acceptance = $acceptance;
$this->recipient = $recipient;
$this->wasAccepted = $wasAccepted;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$subject = $this->wasAccepted
? trans('mail.initiated_accepted')
: trans('mail.initiated_declined');
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.markdown.checkout-acceptance-response',
with: [
'assignedTo' => $this->acceptance->assignedTo,
'introduction' => $this->introduction(),
'item' => $this->acceptance->checkoutable,
'note' => $this->acceptance->note,
'recipient' => $this->recipient,
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
private function introduction(): string
{
return $this->wasAccepted
? trans('mail.following_accepted')
: trans('mail.following_declined');
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -22,6 +23,7 @@ class Accessory extends SnipeModel
protected $presenter = \App\Presenters\AccessoryPresenter::class;
use CompanyableTrait;
use HasUploads;
use Loggable, Presentable;
use SoftDeletes;
@@ -102,24 +104,6 @@ class Accessory extends SnipeModel
];
/**
* Establishes the accessories -> action logs -> uploads relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
->where('item_type', '=', self::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
* Establishes the accessory -> supplier relationship
*
@@ -426,7 +410,6 @@ class Accessory extends SnipeModel
/**
* Query builder scope to order on created_by name
*
*/
public function scopeOrderByCreatedByName($query, $order)
{

View File

@@ -133,44 +133,58 @@ class AccessoryCheckout extends Model
public function advancedTextSearch(Builder $query, array $terms)
{
$userQuery = User::where(function ($query) use ($terms) {
$userQuery = User::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('first_name', 'like', $search_str)
->orWhere('last_name', 'like', $search_str)
->orWhere('note', 'like', $search_str);
}
})->select('id');
}
)->select('id');
$locationQuery = Location::where(function ($query) use ($terms) {
$locationQuery = Location::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
})->select('id');
}
)->select('id');
$assetQuery = Asset::where(function ($query) use ($terms) {
$assetQuery = Asset::where(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('name', 'like', $search_str);
}
})->select('id');
}
)->select('id');
$query->where(function ($query) use ($userQuery) {
$query->where(
function ($query) use ($userQuery) {
$query->where('assigned_type', User::class)
->whereIn('assigned_to', $userQuery);
})->orWhere(function($query) use ($locationQuery) {
}
)->orWhere(
function ($query) use ($locationQuery) {
$query->where('assigned_type', Location::class)
->whereIn('assigned_to', $locationQuery);
})->orWhere(function($query) use ($assetQuery) {
}
)->orWhere(
function ($query) use ($assetQuery) {
$query->where('assigned_type', Asset::class)
->whereIn('assigned_to', $assetQuery);
})->orWhere(function($query) use ($terms) {
}
)->orWhere(
function ($query) use ($terms) {
foreach ($terms as $term) {
$search_str = '%' . $term . '%';
$query->where('note', 'like', $search_str);
}
});
}
);
return $query;
}

View File

@@ -69,8 +69,8 @@ class Actionlog extends SnipeModel
*/
protected $searchableRelations = [
'company' => ['name'],
'adminuser' => ['first_name','last_name','username', 'email'],
'user' => ['first_name','last_name','username', 'email'],
'adminuser' => ['first_name','last_name','username', 'email', 'employee_num'],
'user' => ['first_name','last_name','username', 'email', 'employee_num'],
'assets' => ['asset_tag','name', 'serial', 'order_number', 'notes', 'purchase_date'],
'assets.model' => ['name', 'model_number', 'eol', 'notes'],
'assets.model.category' => ['name', 'notes'],
@@ -102,7 +102,8 @@ class Actionlog extends SnipeModel
public static function boot()
{
parent::boot();
static::creating(function (self $actionlog) {
static::creating(
function (self $actionlog) {
// If the admin is a superadmin, let's see if the target instead has a company.
if (auth()->user() && auth()->user()->isSuperUser()) {
if ($actionlog->target) {
@@ -118,8 +119,8 @@ class Actionlog extends SnipeModel
$actionlog->action_date = Carbon::now();
}
});
}
);
}
@@ -436,7 +437,8 @@ class Actionlog extends SnipeModel
// This is an API call
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
&& (starts_with(request()->header('authorization'), 'Bearer '))) {
&& (starts_with(request()->header('authorization'), 'Bearer '))
) {
return 'api';
}
@@ -450,6 +452,62 @@ class Actionlog extends SnipeModel
}
public function uploads_file_url()
{
switch ($this->item_type) {
case Accessory::class:
return route('show.accessoryfile', [$this->item_id, $this->id]);
case Asset::class:
return route('show/assetfile', [$this->item_id, $this->id]);
case AssetModel::class:
return route('show/modelfile', [$this->item_id, $this->id]);
case Consumable::class:
return route('show/locationsfile', [$this->item_id, $this->id]);
case Component::class:
return route('show.componentfile', [$this->item_id, $this->id]);
case License::class:
return route('show.licensefile', [$this->item_id, $this->id]);
case Location::class:
return route('show/locationsfile', [$this->item_id, $this->id]);
case User::class:
return route('show/userfile', [$this->item_id, $this->id]);
default:
return null;
}
}
public function uploads_file_path()
{
switch ($this->item_type) {
case Accessory::class:
return 'private_uploads/accessories/'.$this->filename;
case Asset::class:
return 'private_uploads/assets/'.$this->filename;
case AssetModel::class:
return 'private_uploads/assetmodels/'.$this->filename;
case Consumable::class:
return 'private_uploads/consumables/'.$this->filename;
case Component::class:
return 'private_uploads/components/'.$this->filename;
case License::class:
return 'private_uploads/licenses/'.$this->filename;
case Location::class:
return 'private_uploads/locations/'.$this->filename;
case User::class:
return 'private_uploads/users/'.$this->filename;
default:
return null;
}
}
// Manually sets $this->source for determineActionSource()
public function setActionSource($source = null): void
{

View File

@@ -7,19 +7,17 @@ use App\Exceptions\CheckoutNotAllowed;
use App\Helpers\Helper;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\Acceptable;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use App\Presenters\AssetPresenter;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
* Model for Assets.
@@ -33,6 +31,7 @@ class Asset extends Depreciable
protected $with = ['model', 'adminuser'];
use CompanyableTrait;
use HasUploads;
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
public const LOCATION = 'location';
@@ -258,6 +257,7 @@ class Asset extends Depreciable
/**
* Returns the warranty expiration date as Carbon object
*
* @return \Carbon\Carbon|null
*/
public function getWarrantyExpiresAttribute()
@@ -307,8 +307,8 @@ class Asset extends Depreciable
// The asset status is not archived and is deployable
if (($this->assetstatus) && ($this->assetstatus->archived == '0')
&& ($this->assetstatus->deployable == '1'))
{
&& ($this->assetstatus->deployable == '1')
) {
return true;
}
@@ -417,7 +417,8 @@ class Asset extends Depreciable
return $this->rules;
}
public function customFieldsForCheckinCheckout($checkin_checkout) {
public function customFieldsForCheckinCheckout($checkin_checkout)
{
// Check to see if any of the custom fields were included on the form and if they have any values
if (($this->model) && ($this->model->fieldset) && ($this->model->fieldset->fields)) {
foreach ($this->model->fieldset->fields as $field) {
@@ -472,26 +473,10 @@ class Asset extends Depreciable
}
/**
* Get uploads for this asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany('\App\Models\Actionlog', 'item_id')
->where('item_type', '=', Asset::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
* Determines whether the asset is checked out to a user
*
* Even though we allow allow for checkout to things beyond users
* Even though we allow for checkout to things beyond users
* this method is an easy way of seeing if we are checked out to a user.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -610,6 +595,7 @@ class Asset extends Depreciable
/**
* This is annoying, but because we don't say "assets" in our route names, we have to make an exception here
*
* @todo - normalize the route names - API endpoint URLS can stay the same
*
* @author [A. Gianotto] [<snipe@snipe.net>]
@@ -761,7 +747,7 @@ class Asset extends Depreciable
*/
public function adminuser()
{
return $this->belongsTo(\App\Models\User::class, 'created_by');
return $this->belongsTo(\App\Models\User::class, 'created_by')->select(['id', 'first_name', 'last_name', 'username', 'created_at', 'created_by'])->withTrashed();
}
@@ -787,7 +773,7 @@ class Asset extends Depreciable
*/
public function model()
{
return $this->belongsTo(\App\Models\AssetModel::class, 'model_id')->withTrashed();
return $this->belongsTo(\App\Models\AssetModel::class, 'model_id')->select(['id', 'name', 'model_number', 'manufacturer_id', 'category_id', 'created_at', 'created_by'])->withTrashed();
}
/**
@@ -807,9 +793,11 @@ class Asset extends Depreciable
->whereNotNull('purchase_date')
->whereNull('deleted_at')
->NotArchived()
->whereRaw('DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) <= DATE_ADD(NOW(), INTERVAL '
->whereRaw(
'DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) <= DATE_ADD(NOW(), INTERVAL '
. $days
. ' DAY) AND DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) > NOW()')
. ' DAY) AND DATE_ADD(`purchase_date`, INTERVAL `warranty_months` MONTH) > NOW()'
)
->orderByRaw('DATE_ADD(`purchase_date`,INTERVAL `warranty_months` MONTH)')
->get();
}
@@ -905,12 +893,10 @@ class Asset extends Depreciable
foreach ($assets as $asset) {
$results = preg_match("/\d+$/", $asset['asset_tag'], $matches);
if ($results)
{
if ($results) {
$number = $matches[0];
if ($number > $max)
{
if ($number > $max) {
$max = $number;
}
}
@@ -1018,7 +1004,8 @@ class Asset extends Depreciable
return false;
}
public function getComponentCost(){
public function getComponentCost()
{
$cost = 0;
foreach($this->components as $component) {
$cost += $component->pivot->assigned_qty*$component->purchase_cost;
@@ -1120,31 +1107,38 @@ class Asset extends Depreciable
/**
* Assigned user
*/
$query = $query->leftJoin('users as assets_users', function ($leftJoin) {
$query = $query->leftJoin(
'users as assets_users', function ($leftJoin) {
$leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', User::class);
});
}
);
foreach ($terms as $term) {
$query = $query
->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.username', 'LIKE', '%'.$term.'%')
->orWhere('assets_users.employee_num', 'LIKE', '%'.$term.'%')
->orWhereMultipleColumns([
->orWhereMultipleColumns(
[
'assets_users.first_name',
'assets_users.last_name',
], $term);
], $term
);
}
/**
* Assigned location
*/
$query = $query->leftJoin('locations as assets_locations', function ($leftJoin) {
$query = $query->leftJoin(
'locations as assets_locations', function ($leftJoin) {
$leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', Location::class);
});
}
);
foreach ($terms as $term) {
@@ -1154,10 +1148,12 @@ class Asset extends Depreciable
/**
* Assigned assets
*/
$query = $query->leftJoin('assets as assigned_assets', function ($leftJoin) {
$query = $query->leftJoin(
'assets as assigned_assets', function ($leftJoin) {
$leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', self::class);
});
}
);
foreach ($terms as $term) {
$query = $query->orWhere('assigned_assets.name', 'LIKE', '%'.$term.'%');
@@ -1191,11 +1187,13 @@ class Asset extends Depreciable
public function scopePending($query)
{
return $query->whereHas('assetstatus', function ($query) {
return $query->whereHas(
'assetstatus', function ($query) {
$query->where('deployable', '=', 0)
->where('pending', '=', 1)
->where('archived', '=', 0);
});
}
);
}
@@ -1209,23 +1207,35 @@ class Asset extends Depreciable
public function scopeAssetsByLocation($query, $location)
{
return $query->where(function ($query) use ($location) {
$query->whereHas('assignedTo', function ($query) use ($location) {
$query->where([
return $query->where(
function ($query) use ($location) {
$query->whereHas(
'assignedTo', function ($query) use ($location) {
$query->where(
[
['users.location_id', '=', $location->id],
['assets.assigned_type', '=', User::class],
])->orWhere([
]
)->orWhere(
[
['locations.id', '=', $location->id],
['assets.assigned_type', '=', Location::class],
])->orWhere([
]
)->orWhere(
[
['assets.rtd_location_id', '=', $location->id],
['assets.assigned_type', '=', self::class],
]);
})->orWhere(function ($query) use ($location) {
]
);
}
)->orWhere(
function ($query) use ($location) {
$query->where('assets.rtd_location_id', '=', $location->id);
$query->whereNull('assets.assigned_to');
});
});
}
);
}
);
}
@@ -1240,11 +1250,13 @@ class Asset extends Depreciable
public function scopeRTD($query)
{
return $query->whereNull('assets.assigned_to')
->whereHas('assetstatus', function ($query) {
->whereHas(
'assetstatus', function ($query) {
$query->where('deployable', '=', 1)
->where('pending', '=', 0)
->where('archived', '=', 0);
});
}
);
}
/**
@@ -1257,11 +1269,13 @@ class Asset extends Depreciable
public function scopeUndeployable($query)
{
return $query->whereHas('assetstatus', function ($query) {
return $query->whereHas(
'assetstatus', function ($query) {
$query->where('deployable', '=', 0)
->where('pending', '=', 0)
->where('archived', '=', 0);
});
}
);
}
/**
@@ -1274,9 +1288,11 @@ class Asset extends Depreciable
public function scopeNotArchived($query)
{
return $query->whereHas('assetstatus', function ($query) {
return $query->whereHas(
'assetstatus', function ($query) {
$query->where('archived', '=', 0);
});
}
);
}
/**
@@ -1353,11 +1369,15 @@ class Asset extends Depreciable
public function scopeDueOrOverdueForAudit($query, $settings)
{
return $query->where(function ($query) {
return $query->where(
function ($query) {
$query->OverdueForAudit();
})->orWhere(function ($query) use ($settings) {
}
)->orWhere(
function ($query) use ($settings) {
$query->DueForAudit($settings);
});
}
);
}
@@ -1408,11 +1428,15 @@ class Asset extends Depreciable
*/
public function scopeDueOrOverdueForCheckin($query, $settings)
{
return $query->where(function ($query) {
return $query->where(
function ($query) {
$query->OverdueForCheckin();
})->orWhere(function ($query) use ($settings) {
}
)->orWhere(
function ($query) use ($settings) {
$query->DueForCheckin($settings);
});
}
);
}
@@ -1432,9 +1456,11 @@ class Asset extends Depreciable
{
if (Setting::getSettings()->show_archived_in_list!=1) {
return $query->whereHas('assetstatus', function ($query) {
return $query->whereHas(
'assetstatus', function ($query) {
$query->where('archived', '=', 0);
});
}
);
} else {
return $query;
}
@@ -1451,11 +1477,13 @@ class Asset extends Depreciable
public function scopeArchived($query)
{
return $query->whereHas('assetstatus', function ($query) {
return $query->whereHas(
'assetstatus', function ($query) {
$query->where('deployable', '=', 0)
->where('pending', '=', 0)
->where('archived', '=', 1);
});
}
);
}
/**
@@ -1484,12 +1512,16 @@ class Asset extends Depreciable
$table = $query->getModel()->getTable();
return Company::scopeCompanyables($query->where($table.'.requestable', '=', 1))
->whereHas('assetstatus', function ($query) {
$query->where(function ($query) {
->whereHas(
'assetstatus', function ($query) {
$query->where(
function ($query) {
$query->where('deployable', '=', 1)
->where('archived', '=', 0); // you definitely can't request something that's archived
})->orWhere('pending', '=', 1); // we've decided that even though an asset may be 'pending', you can still request it
});
}
)->orWhere('pending', '=', 1); // we've decided that even though an asset may be 'pending', you can still request it
}
);
}
@@ -1558,49 +1590,75 @@ class Asset extends Depreciable
{
$search = explode(' OR ', $search);
return $query->leftJoin('users as assets_users', function ($leftJoin) {
return $query->leftJoin(
'users as assets_users', function ($leftJoin) {
$leftJoin->on('assets_users.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', User::class);
})->leftJoin('locations as assets_locations', function ($leftJoin) {
}
)->leftJoin(
'locations as assets_locations', function ($leftJoin) {
$leftJoin->on('assets_locations.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', Location::class);
})->leftJoin('assets as assigned_assets', function ($leftJoin) {
}
)->leftJoin(
'assets as assigned_assets', function ($leftJoin) {
$leftJoin->on('assigned_assets.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', self::class);
})->where(function ($query) use ($search) {
}
)->where(
function ($query) use ($search) {
foreach ($search as $search) {
$query->whereHas('model', function ($query) use ($search) {
$query->whereHas('category', function ($query) use ($search) {
$query->where(function ($query) use ($search) {
$query->whereHas(
'model', function ($query) use ($search) {
$query->whereHas(
'category', function ($query) use ($search) {
$query->where(
function ($query) use ($search) {
$query->where('categories.name', 'LIKE', '%'.$search.'%')
->orWhere('models.name', 'LIKE', '%'.$search.'%')
->orWhere('models.model_number', 'LIKE', '%'.$search.'%');
});
});
})->orWhereHas('model', function ($query) use ($search) {
$query->whereHas('manufacturer', function ($query) use ($search) {
$query->where(function ($query) use ($search) {
}
);
}
);
}
)->orWhereHas(
'model', function ($query) use ($search) {
$query->whereHas(
'manufacturer', function ($query) use ($search) {
$query->where(
function ($query) use ($search) {
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
});
});
})->orWhere(function ($query) use ($search) {
}
);
}
);
}
)->orWhere(
function ($query) use ($search) {
$query->where('assets_users.first_name', 'LIKE', '%'.$search.'%')
->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%')
->orWhereMultipleColumns([
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
->orWhere('assets_users.jobtitle', 'LIKE', '%'.$search.'%')
->orWhereMultipleColumns(
[
'assets_users.first_name',
'assets_users.last_name',
], $search)
->orWhere('assets_users.username', 'LIKE', '%'.$search.'%')
'assets_users.jobtitle',
], $search
)
->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%')
->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%');
})->orWhere('assets.name', 'LIKE', '%'.$search.'%')
}
)->orWhere('assets.name', 'LIKE', '%'.$search.'%')
->orWhere('assets.asset_tag', 'LIKE', '%'.$search.'%')
->orWhere('assets.serial', 'LIKE', '%'.$search.'%')
->orWhere('assets.order_number', 'LIKE', '%'.$search.'%')
->orWhere('assets.notes', 'LIKE', '%'.$search.'%');
}
})->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
)->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
/**
@@ -1614,13 +1672,17 @@ class Asset extends Depreciable
*/
public function scopeCheckedOutToTargetInDepartment($query, $search)
{
return $query->leftJoin('users as assets_dept_users', function ($leftJoin) {
return $query->leftJoin(
'users as assets_dept_users', function ($leftJoin) {
$leftJoin->on('assets_dept_users.id', '=', 'assets.assigned_to')
->where('assets.assigned_type', '=', User::class);
})->where(function ($query) use ($search) {
}
)->where(
function ($query) use ($search) {
$query->whereIn('assets_dept_users.department_id', $search);
})->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
)->withTrashed()->whereNull('assets.deleted_at'); //workaround for laravel bug
}
@@ -1635,7 +1697,8 @@ class Asset extends Depreciable
*/
public function scopeByFilter($query, $filter)
{
return $query->where(function ($query) use ($filter) {
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $key => $search_val) {
$fieldname = str_replace('custom_fields.', '', $key);
@@ -1670,86 +1733,124 @@ class Asset extends Depreciable
}
if ($fieldname == 'status_label') {
$query->whereHas('assetstatus', function ($query) use ($search_val) {
$query->whereHas(
'assetstatus', function ($query) use ($search_val) {
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
});
}
);
}
if ($fieldname == 'location') {
$query->whereHas('location', function ($query) use ($search_val) {
$query->whereHas(
'location', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
});
}
);
}
if ($fieldname == 'rtd_location') {
$query->whereHas('defaultLoc', function ($query) use ($search_val) {
$query->whereHas(
'defaultLoc', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
});
}
);
}
if ($fieldname =='assigned_to') {
$query->whereHasMorph('assignedTo', [User::class], function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
$query->whereHasMorph(
'assignedTo', [User::class], function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%');
});
});
}
);
}
);
}
if ($fieldname == 'manufacturer') {
$query->whereHas('model', function ($query) use ($search_val) {
$query->whereHas('manufacturer', function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
$query->whereHas(
'model', function ($query) use ($search_val) {
$query->whereHas(
'manufacturer', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
});
});
});
}
);
}
);
}
);
}
if ($fieldname == 'category') {
$query->whereHas('model', function ($query) use ($search_val) {
$query->whereHas('category', function ($query) use ($search_val) {
$query->where(function ($query) use ($search_val) {
$query->whereHas(
'model', function ($query) use ($search_val) {
$query->whereHas(
'category', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->where('categories.name', 'LIKE', '%'.$search_val.'%')
->orWhere('models.name', 'LIKE', '%'.$search_val.'%')
->orWhere('models.model_number', 'LIKE', '%'.$search_val.'%');
});
});
});
}
);
}
);
}
);
}
if ($fieldname == 'model') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('model', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->whereHas(
'model', function ($query) use ($search_val) {
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
});
});
}
);
}
);
}
if ($fieldname == 'model_number') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('model', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->whereHas(
'model', function ($query) use ($search_val) {
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
});
});
}
);
}
);
}
if ($fieldname == 'company') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('company', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->whereHas(
'company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
});
});
}
);
}
);
}
if ($fieldname == 'supplier') {
$query->where(function ($query) use ($search_val) {
$query->whereHas('supplier', function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
$query->whereHas(
'supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
});
});
}
);
}
);
}
@@ -1773,11 +1874,11 @@ class Asset extends Depreciable
* against those relationships earlier in this method.
*
* - snipe
*
*/
if (($fieldname!='category') && ($fieldname!='model_number') && ($fieldname!='rtd_location') && ($fieldname!='location') && ($fieldname!='supplier')
&& ($fieldname!='status_label') && ($fieldname!='assigned_to') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')) {
&& ($fieldname!='status_label') && ($fieldname!='assigned_to') && ($fieldname!='model') && ($fieldname!='company') && ($fieldname!='manufacturer')
) {
$query->where('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
}
@@ -1785,7 +1886,8 @@ class Asset extends Depreciable
}
});
}
);
}
@@ -1949,6 +2051,7 @@ class Asset extends Depreciable
/**
* Query builder scope to order on default
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
@@ -1973,6 +2076,19 @@ class Asset extends Depreciable
return $query->leftJoin('suppliers as suppliers_assets', 'assets.supplier_id', '=', 'suppliers_assets.id')->orderBy('suppliers_assets.name', $order);
}
/**
* Query builder scope to order on supplier name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderByJobTitle($query, $order)
{
return $query->leftJoin('users as users_sort', 'assets.assigned_to', '=', 'users_sort.id')->select('assets.*')->orderBy('users_sort.jobtitle', $order);
}
/**
* Query builder scope to search on location ID
*
@@ -1983,17 +2099,22 @@ class Asset extends Depreciable
*/
public function scopeByLocationId($query, $search)
{
return $query->where(function ($query) use ($search) {
$query->whereHas('location', function ($query) use ($search) {
return $query->where(
function ($query) use ($search) {
$query->whereHas(
'location', function ($query) use ($search) {
$query->where('locations.id', '=', $search);
});
});
}
);
}
);
}
/**
* Query builder scope to search on depreciation name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $search Search term
*

View File

@@ -266,6 +266,21 @@ class AssetMaintenance extends Model implements ICompanyableChild
->orderBy('maintained_asset_status.name', $order);
}
/**
* Query builder scope to order on status label name
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $order Order
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOrderLocationName($query, $order)
{
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
->leftjoin('locations as maintained_asset_location', 'maintained_asset_location.id', '=', 'maintained_asset.location_id')
->orderBy('maintained_asset_location.name', $order);
}
/**
* Query builder scope to order on the user that created it
*/

View File

@@ -2,6 +2,7 @@
namespace App\Models;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -24,6 +25,7 @@ class AssetModel extends SnipeModel
use SoftDeletes;
use Loggable, Requestable, Presentable;
use TwoColumnUniqueUndeletedTrait;
use HasUploads;
/**
* Whether the model should inject its identifier to the unique
@@ -209,21 +211,6 @@ class AssetModel extends SnipeModel
&& ($this->deleted_at == '');
}
/**
* Get uploads for this model
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany('\App\Models\Actionlog', 'item_id')
->where('item_type', '=', AssetModel::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
* Get user who created the item
@@ -287,16 +274,24 @@ class AssetModel extends SnipeModel
{
return $query->where('models.name', 'LIKE', "%$search%")
->orWhere('model_number', 'LIKE', "%$search%")
->orWhere(function ($query) use ($search) {
$query->whereHas('category', function ($query) use ($search) {
->orWhere(
function ($query) use ($search) {
$query->whereHas(
'category', function ($query) use ($search) {
$query->where('categories.name', 'LIKE', '%'.$search.'%');
});
})
->orWhere(function ($query) use ($search) {
$query->whereHas('manufacturer', function ($query) use ($search) {
}
);
}
)
->orWhere(
function ($query) use ($search) {
$query->whereHas(
'manufacturer', function ($query) use ($search) {
$query->where('manufacturers.name', 'LIKE', '%'.$search.'%');
});
});
}
);
}
);
}
/**
@@ -332,7 +327,6 @@ class AssetModel extends SnipeModel
/**
* Query builder scope to order on created_by name
*
*/
public function scopeOrderByCreatedByName($query, $order)
{

View File

@@ -32,6 +32,7 @@ class Category extends SnipeModel
protected $hidden = ['created_by', 'deleted_at'];
protected $casts = [
'alert_on_response' => 'boolean',
'created_by' => 'integer',
];
@@ -69,6 +70,7 @@ class Category extends SnipeModel
'eula_text',
'name',
'require_acceptance',
'alert_on_response',
'use_default_eula',
'created_by',
'notes',

View File

@@ -15,6 +15,7 @@ class CheckoutAcceptance extends Model
protected $casts = [
'accepted_at' => 'datetime',
'declined_at' => 'datetime',
'alert_on_response_id' => 'integer',
];
/**
@@ -115,6 +116,7 @@ class CheckoutAcceptance extends Model
/**
* Filter checkout acceptences by the user
*
* @param Illuminate\Database\Eloquent\Builder $query
* @param User $user
* @return \Illuminate\Database\Eloquent\Builder
@@ -126,6 +128,7 @@ class CheckoutAcceptance extends Model
/**
* Filter to only get pending acceptances
*
* @param Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/

View File

@@ -18,6 +18,8 @@ use Illuminate\Support\Facades\Schema;
final class Company extends SnipeModel
{
use HasFactory;
use CompanyableTrait;
protected $table = 'companies';
@@ -146,10 +148,10 @@ final class Company extends SnipeModel
if (!is_string($companyable)) {
$company_table = $companyable->getModel()->getTable();
try {
// This is primary for the gate:allows-check in location->isDeletable()
// This is primarily for the gate:allows-check in location->isDeletable()
// Locations don't have a company_id so without this it isn't possible to delete locations with FullMultipleCompanySupport enabled
// because this function is called by SnipePermissionsPolicy->before()
if (!$companyable instanceof Company && !Schema::hasColumn($company_table, 'company_id')) {
if (!Schema::hasColumn($company_table, 'company_id')) {
return true;
}
@@ -163,8 +165,15 @@ final class Company extends SnipeModel
// Log::warning('Companyable is '.$companyable);
$current_user_company_id = auth()->user()->company_id;
$companyable_company_id = $companyable->company_id;
return $current_user_company_id == null || $current_user_company_id == $companyable_company_id || auth()->user()->isSuperUser();
// Set this to check companyable on company
if ($companyable instanceof Company) {
$companyable_company_id = $companyable->id;
}
return ($current_user_company_id == null) || ($current_user_company_id == $companyable_company_id) || auth()->user()->isSuperUser();
}
return false;
}
@@ -263,7 +272,7 @@ final class Company extends SnipeModel
*/
public static function scopeCompanyables($query, $column = 'company_id', $table_name = null)
{
// If not logged in and hitting this, assume we are on the command line and don't scope?'
// If not logged in and hitting this, assume we are on the command line and don't scope?
if (! static::isFullMultipleCompanySupportEnabled() || (Auth::hasUser() && auth()->user()->isSuperUser()) || (! Auth::hasUser())) {
return $query;
} else {
@@ -280,11 +289,16 @@ final class Company extends SnipeModel
private static function scopeCompanyablesDirectly($query, $column = 'company_id', $table_name = null)
{
$company_id = null;
// Get the company ID of the logged-in user, or set it to null if there is no company associated with the user
if (Auth::hasUser()) {
$company_id = auth()->user()->company_id;
} else {
$company_id = null;
}
// If we are scoping the companies table itself, look for the company.id
if ($query->getModel()->getTable() == 'companies') {
return $query->where('companies.id', '=', $company_id);
}
@@ -297,6 +311,8 @@ final class Company extends SnipeModel
return $query->where($table.$column, '=', $company_id);
}
}
public function adminuser()
@@ -324,17 +340,18 @@ final class Company extends SnipeModel
return $query;
} else {
$f = function ($q) {
Log::debug('scopeCompanyablesDirectly firing ');
static::scopeCompanyablesDirectly($q);
};
$q = $query->where(function ($q) use ($companyable_names, $f) {
$q = $query->where(
function ($q) use ($companyable_names, $f) {
$q2 = $q->whereHas($companyable_names[0], $f);
for ($i = 1; $i < count($companyable_names); $i++) {
$q2 = $q2->orWhereHas($companyable_names[$i], $f);
}
});
}
);
return $q;
}

View File

@@ -2,6 +2,7 @@
namespace App\Models;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -20,6 +21,7 @@ class Component extends SnipeModel
protected $presenter = \App\Presenters\ComponentPresenter::class;
use CompanyableTrait;
use HasUploads;
use Loggable, Presentable;
use SoftDeletes;
protected $casts = [
@@ -113,21 +115,6 @@ class Component extends SnipeModel
&& ($this->deleted_at == '');
}
/**
* Establishes the components -> action logs -> uploads relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany(\App\Models\Actionlog::class, 'item_id')
->where('item_type', '=', self::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**
@@ -252,7 +239,8 @@ class Component extends SnipeModel
*
* This allows us to get the assets with assigned components without the company restriction
*/
public function uncontrainedAssets() {
public function uncontrainedAssets()
{
return $this->belongsToMany(\App\Models\Asset::class, 'components_assets')
->withPivot('id', 'assigned_qty', 'created_at', 'created_by', 'note')

View File

@@ -4,6 +4,7 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -29,6 +30,7 @@ class Consumable extends SnipeModel
use Loggable, Presentable;
use SoftDeletes;
use Acceptable;
use HasUploads;
protected $table = 'consumables';
protected $casts = [
@@ -111,21 +113,6 @@ class Consumable extends SnipeModel
];
/**
* Establishes the components -> action logs -> uploads relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v6.1.13]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function uploads()
{
return $this->hasMany(Actionlog::class, 'item_id')
->where('item_type', '=', self::class)
->where('action_type', '=', 'uploaded')
->whereNotNull('filename')
->orderBy('created_at', 'desc');
}
/**

View File

@@ -126,7 +126,8 @@ class CustomField extends Model
public static function boot()
{
parent::boot();
self::created(function ($custom_field) {
self::created(
function ($custom_field) {
// Column already exists on the assets table - nothing to do here.
// This *shouldn't* happen in the wild.
@@ -135,16 +136,20 @@ class CustomField extends Model
}
// Update the column name in the assets table
Schema::table(self::$table_name, function ($table) use ($custom_field) {
Schema::table(
self::$table_name, function ($table) use ($custom_field) {
$table->text($custom_field->convertUnicodeDbSlug())->nullable();
});
}
);
// Update the db_column property in the custom fields table
$custom_field->db_column = $custom_field->convertUnicodeDbSlug();
$custom_field->save();
});
}
);
self::updating(function ($custom_field) {
self::updating(
function ($custom_field) {
// Column already exists on the assets table - nothing to do here.
if ($custom_field->isDirty('name')) {
@@ -153,9 +158,11 @@ class CustomField extends Model
}
// Rename the field if the name has changed
Schema::table(self::$table_name, function ($table) use ($custom_field) {
Schema::table(
self::$table_name, function ($table) use ($custom_field) {
$table->renameColumn($custom_field->convertUnicodeDbSlug($custom_field->getOriginal('name')), $custom_field->convertUnicodeDbSlug());
});
}
);
// Save the updated column name to the custom fields table
$custom_field->db_column = $custom_field->convertUnicodeDbSlug();
@@ -165,14 +172,19 @@ class CustomField extends Model
}
return true;
});
}
);
// Drop the assets column if we've deleted it from custom fields
self::deleting(function ($custom_field) {
return Schema::table(self::$table_name, function ($table) use ($custom_field) {
self::deleting(
function ($custom_field) {
return Schema::table(
self::$table_name, function ($table) use ($custom_field) {
$table->dropColumn($custom_field->db_column);
});
});
}
);
}
);
}
/**
@@ -262,11 +274,15 @@ class CustomField extends Model
*/
public function defaultValue($modelId)
{
return $this->defaultValues->filter(function ($item) use ($modelId) {
return $this->defaultValues->filter(
function ($item) use ($modelId) {
return $item->pivot->asset_model_id == $modelId;
})->map(function ($item) {
}
)->map(
function ($item) {
return $item->pivot->default_value;
})->first();
}
)->first();
}
/**
@@ -402,6 +418,7 @@ class CustomField extends Model
/**
* Get validation rules for custom fields to use with Validator
*
* @author [V. Cordes] [<volker@fdatek.de>]
* @param int $id
* @since [v4.1.10]
@@ -418,6 +435,7 @@ class CustomField extends Model
/**
* Check to see if there is a custom regex format type
*
* @see https://github.com/grokability/snipe-it/issues/5896
*
* @author Wes Hulette <jwhulette@gmail.com>

View File

@@ -20,6 +20,7 @@ class CustomFieldset extends Model
/**
* Validation rules
*
* @var array
*/
public $rules = [
@@ -104,8 +105,9 @@ class CustomFieldset extends Model
foreach ($this->fields as $field) {
$rule = [];
if (($field->field_encrypted != '1') ||
(($field->field_encrypted == '1') && (Gate::allows('admin')))) {
if (($field->field_encrypted != '1')
|| (($field->field_encrypted == '1') && (Gate::allows('admin')))
) {
$rule[] = ($field->pivot->required == '1') ? 'required' : 'nullable';
}

View File

@@ -93,7 +93,8 @@ class Depreciable extends SnipeModel
return $current_value;
}
public function getMonthlyDepreciation(){
public function getMonthlyDepreciation()
{
return ($this->purchase_cost-$this->calculateDepreciation())/$this->get_depreciation()->months;

View File

@@ -16,7 +16,7 @@ class Depreciation extends SnipeModel
// Declare the rules for the form validation
protected $rules = [
'name' => 'required|min:3|max:255|unique:depreciations,name',
'months' => 'required|max:3600|integer|gt:0',
'months' => 'required|max:3600|integer',
];
/**

View File

@@ -38,7 +38,8 @@ class DefaultLabel extends RectangleSheet
private int $rows;
public function __construct() {
public function __construct()
{
$settings = Setting::getSettings();
$this->textSize = Helper::convertUnit($settings->labels_fontsize, 'pt', 'in');
@@ -74,41 +75,116 @@ class DefaultLabel extends RectangleSheet
}
public function getUnit() { return 'in'; }
public function getUnit()
{
return 'in';
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginBottom; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginRight; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginBottom;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginRight;
}
public function getColumns() { return $this->columns; }
public function getRows() { return $this->rows; }
public function getLabelBorder() { return 0; }
public function getColumns()
{
return $this->columns;
}
public function getRows()
{
return $this->rows;
}
public function getLabelBorder()
{
return 0;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelMarginTop() { return 0; }
public function getLabelMarginBottom() { return 0; }
public function getLabelMarginLeft() { return 0; }
public function getLabelMarginRight() { return 0; }
public function getLabelMarginTop()
{
return 0;
}
public function getLabelMarginBottom()
{
return 0;
}
public function getLabelMarginLeft()
{
return 0;
}
public function getLabelMarginRight()
{
return 0;
}
public function getLabelColumnSpacing() { return $this->labelSpacingH; }
public function getLabelRowSpacing() { return $this->labelSpacingV; }
public function getLabelColumnSpacing()
{
return $this->labelSpacingH;
}
public function getLabelRowSpacing()
{
return $this->labelSpacingV;
}
public function getSupportAssetTag() { return false; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 4; }
public function getSupportTitle() { return true; }
public function getSupportLogo() { return true; }
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 4;
}
public function getSupportTitle()
{
return true;
}
public function getSupportLogo()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$asset = $record->get('asset');
$settings = Setting::getSettings();

View File

@@ -5,21 +5,30 @@ namespace App\Models\Labels;
use App\Models\Asset;
use Illuminate\Support\Collection;
class Field {
class Field
{
protected Collection $options;
public function getOptions() { return $this->options; }
public function setOptions($options) {
public function getOptions()
{
return $this->options;
}
public function setOptions($options)
{
$tempCollect = collect($options);
if (!$tempCollect->contains(fn($o) => !is_subclass_of($o, FieldOption::class))) {
$this->options = $options;
}
}
public function toArray(Asset $asset) { return Field::makeArray($this, $asset); }
public function toArray(Asset $asset)
{
return Field::makeArray($this, $asset);
}
/* Statics */
public static function makeArray(Field $field, Asset $asset) {
public static function makeArray(Field $field, Asset $asset)
{
return $field->getOptions()
// filter out any FieldOptions that are accidentally null
->filter()
@@ -27,11 +36,13 @@ class Field {
->filter(fn($result) => $result['value'] != null);
}
public static function makeString(Field $option) {
public static function makeString(Field $option)
{
return implode('|', $option->getOptions());
}
public static function fromString(string $theString) {
public static function fromString(string $theString)
{
$field = new Field();
$field->options = collect(explode('|', $theString))
->filter(fn($optionString) => !empty($optionString))

View File

@@ -5,14 +5,22 @@ namespace App\Models\Labels;
use App\Models\Asset;
use Illuminate\Support\Collection;
class FieldOption {
class FieldOption
{
protected string $label;
public function getLabel() { return $this->label; }
public function getLabel()
{
return $this->label;
}
protected string $dataSource;
public function getDataSource() { return $this->dataSource; }
public function getDataSource()
{
return $this->dataSource;
}
public function getValue(Asset $asset) {
public function getValue(Asset $asset)
{
$dataPath = collect(explode('.', $this->dataSource));
// assignedTo directly on the asset is a special case where
@@ -33,18 +41,29 @@ class FieldOption {
return $asset->purchase_date ? $asset->purchase_date->format('Y-m-d') : null;
}
return $dataPath->reduce(function ($myValue, $path) {
try { return $myValue ? $myValue->{$path} : ${$myValue}; }
catch (\Exception $e) { return $myValue; }
}, $asset);
return $dataPath->reduce(
function ($myValue, $path) {
try { return $myValue ? $myValue->{$path} : ${$myValue};
}
catch (\Exception $e) { return $myValue;
}
}, $asset
);
}
public function toArray(Asset $asset=null) { return FieldOption::makeArray($this, $asset); }
public function toString() { return FieldOption::makeString($this); }
public function toArray(Asset $asset=null)
{
return FieldOption::makeArray($this, $asset);
}
public function toString()
{
return FieldOption::makeString($this);
}
/* Statics */
public static function makeArray(FieldOption $option, Asset $asset=null) {
public static function makeArray(FieldOption $option, Asset $asset=null)
{
return [
'label' => $option->getLabel(),
'dataSource' => $option->getDataSource(),
@@ -52,11 +71,13 @@ class FieldOption {
];
}
public static function makeString(FieldOption $option) {
public static function makeString(FieldOption $option)
{
return $option->getLabel() . '=' . $option->getDataSource();
}
public static function fromString(string $theString) {
public static function fromString(string $theString)
{
$parts = explode('=', $theString);
if (count($parts) == 2) {
$option = new FieldOption();

View File

@@ -32,7 +32,8 @@ abstract class Label
*
* @return int
*/
public function getRotation() {
public function getRotation()
{
return 0;
}
@@ -141,11 +142,14 @@ abstract class Label
* @param TCPDF $pdf The TCPDF instance
* @param Collection $data The data
*/
public function writeAll(TCPDF $pdf, Collection $data) {
$data->each(function ($record, $index) use ($pdf) {
public function writeAll(TCPDF $pdf, Collection $data)
{
$data->each(
function ($record, $index) use ($pdf) {
$pdf->AddPage();
$this->write($pdf, $record);
});
}
);
}
/**
@@ -153,7 +157,8 @@ abstract class Label
*
* @return string
*/
public final function getName() {
public final function getName()
{
$refClass = new \ReflectionClass(Label::class);
return str_replace($refClass->getNamespaceName() . '\\', '', get_class($this));
}
@@ -165,7 +170,8 @@ abstract class Label
*
* @return string
*/
public final function getOrientation() {
public final function getOrientation()
{
return ($this->getWidth() >= $this->getHeight()) ? 'L' : 'P';
}
@@ -174,7 +180,8 @@ abstract class Label
*
* @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ]
*/
public final function getPrintableArea() {
public final function getPrintableArea()
{
return (object)[
'x1' => $this->getMarginLeft(),
'y1' => $this->getMarginTop(),
@@ -202,7 +209,8 @@ abstract class Label
* @param int $border Thickness of border. Default = 0.
* @param int $spacing Letter spacing. Default = 0.
*/
public final function writeText(TCPDF $pdf, $text, $x, $y, $font=null, $style=null, $size=null, $align='L', $width=null, $height=null, $squash=false, $border=0, $spacing=0) {
public final function writeText(TCPDF $pdf, $text, $x, $y, $font=null, $style=null, $size=null, $align='L', $width=null, $height=null, $squash=false, $border=0, $spacing=0)
{
$prevFamily = $pdf->getFontFamily();
$prevStyle = $pdf->getFontStyle();
$prevSizePt = $pdf->getFontSizePt();
@@ -211,13 +219,15 @@ abstract class Label
$fontFamily = !empty($font) ? $font : $prevFamily;
$fontStyle = !empty($style) ? $style : $prevStyle;
if ($size) $fontSizePt = Helper::convertUnit($size, $this->getUnit(), 'pt', true);
else $fontSizePt = $prevSizePt;
if ($size) { $fontSizePt = Helper::convertUnit($size, $this->getUnit(), 'pt', true);
} else { $fontSizePt = $prevSizePt;
}
$pdf->SetFontSpacing($spacing);
$parts = collect(explode('**', $text))
->map(function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt) {
->map(
function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt) {
$modStyle = ($index % 2 == 1) ? 'B' : $fontStyle;
$pdf->setFont($fontFamily, $modStyle, $fontSizePt);
return [
@@ -227,17 +237,24 @@ abstract class Label
'font_style' => $modStyle,
'font_size' => $fontSizePt,
];
});
}
);
$textWidth = $parts->reduce(function ($carry, $part) { return $carry += $part['text_width']; });
$textWidth = $parts->reduce(
function ($carry, $part) {
return $carry += $part['text_width'];
}
);
$cellWidth = !empty($width) ? $width : $textWidth;
if ($squash && ($textWidth > 0)) {
$scaleFactor = min(1.0, $cellWidth / $textWidth);
$parts = $parts->map(function ($part, $index) use ($scaleFactor) {
$parts = $parts->map(
function ($part, $index) use ($scaleFactor) {
$part['text_width'] = $part['text_width'] * $scaleFactor;
return $part;
});
}
);
}
$cellHeight = !empty($height) ? $height : Helper::convertUnit($fontSizePt, 'pt', $this->getUnit());
@@ -249,18 +266,23 @@ abstract class Label
}
switch($align) {
case 'R': $startX = ($x + $cellWidth) - min($cellWidth, $textWidth); break;
case 'C': $startX = ($x + ($cellWidth / 2)) - (min($cellWidth, $textWidth) / 2); break;
case 'R': $startX = ($x + $cellWidth) - min($cellWidth, $textWidth);
break;
case 'C': $startX = ($x + ($cellWidth / 2)) - (min($cellWidth, $textWidth) / 2);
break;
case 'L':
default: $startX = $x; break;
default: $startX = $x;
break;
}
$parts->reduce(function ($currentX, $part) use ($pdf, $y, $cellHeight) {
$parts->reduce(
function ($currentX, $part) use ($pdf, $y, $cellHeight) {
$pdf->SetXY($currentX, $y);
$pdf->setFont($part['font_family'], $part['font_style'], $part['font_size']);
$pdf->Cell($part['text_width'], $cellHeight, $part['text'], 0, 0, '', false, '', 1, true);
return $currentX += $part['text_width'];
}, $startX);
}, $startX
);
$pdf->SetFont($prevFamily, $prevStyle, $prevSizePt);
$pdf->SetFontSpacing(0);
@@ -284,12 +306,15 @@ abstract class Label
*
* @return array Returns the final calculated size [w,h]
*/
public final function writeImage(TCPDF $pdf, $image, $x, $y, $width=null, $height=null, $halign='L', $valign='L', $dpi=300, $resize=false, $stretch=false, $border=0) {
public final function writeImage(TCPDF $pdf, $image, $x, $y, $width=null, $height=null, $halign='L', $valign='L', $dpi=300, $resize=false, $stretch=false, $border=0)
{
if (empty($image)) return [0,0];
if (empty($image)) { return [0,0];
}
$imageInfo = getimagesize($image);
if (!$imageInfo) return [0,0]; // TODO: SVG or other
if (!$imageInfo) { return [0,0]; // TODO: SVG or other
}
$imageWidthPx = $imageInfo[0];
$imageHeightPx = $imageInfo[1];
@@ -342,18 +367,24 @@ abstract class Label
// Horizontal Position
switch ($halign) {
case 'R': $originX = ($x + $containerWidth) - $outputWidth; break;
case 'C': $originX = ($x + ($containerWidth / 2)) - ($outputWidth / 2); break;
case 'R': $originX = ($x + $containerWidth) - $outputWidth;
break;
case 'C': $originX = ($x + ($containerWidth / 2)) - ($outputWidth / 2);
break;
case 'L':
default: $originX = $x; break;
default: $originX = $x;
break;
}
// Vertical Position
switch ($valign) {
case 'B': $originY = ($y + $containerHeight) - $outputHeight; break;
case 'C': $originY = ($y + ($containerHeight / 2)) - ($outputHeight / 2); break;
case 'B': $originY = ($y + $containerHeight) - $outputHeight;
break;
case 'C': $originY = ($y + ($containerHeight / 2)) - ($outputHeight / 2);
break;
case 'T':
default: $originY = $y; break;
default: $originY = $y;
break;
}
// Actual Image
@@ -381,8 +412,10 @@ abstract class Label
* @param float $width The container width
* @param float $height The container height
*/
public final function write1DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) {
if (empty($value)) return;
public final function write1DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height)
{
if (empty($value)) { return;
}
try {
$pdf->write1DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
} catch (\Exception|TypeError $e) {
@@ -401,8 +434,10 @@ abstract class Label
* @param float $width The container width
* @param float $height The container height
*/
public final function write2DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height) {
if (empty($value)) return;
public final function write2DBarcode(TCPDF $pdf, $value, $type, $x, $y, $width, $height)
{
if (empty($value)) { return;
}
$pdf->write2DBarcode($value, $type, $x, $y, $width, $height, null, ['stretch'=>true]);
}
@@ -411,127 +446,180 @@ abstract class Label
/**
* Checks the template is internally valid
*/
public final function validate() : void {
public final function validate() : void
{
$this->validateUnits();
$this->validateSize();
$this->validateMargins();
$this->validateSupport();
}
private function validateUnits() : void {
private function validateUnits() : void
{
$validUnits = [ 'pt', 'mm', 'cm', 'in' ];
$unit = $this->getUnit();
if (!in_array(strtolower($unit), $validUnits)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_value', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_value', [
'name' => 'getUnit()',
'expected' => '[ \''.implode('\', \'', $validUnits).'\' ]',
'actual' => '\''.$unit.'\''
]));
]
)
);
}
}
private function validateSize() : void {
private function validateSize() : void
{
$width = $this->getWidth();
if (!is_numeric($width) || is_string($width)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getWidth()',
'expected' => 'float',
'actual' => gettype($width)
]));
]
)
);
}
$height = $this->getHeight();
if (!is_numeric($height) || is_string($height)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getHeight()',
'expected' => 'float',
'actual' => gettype($height)
]));
]
)
);
}
}
private function validateMargins() : void {
private function validateMargins() : void
{
$marginTop = $this->getMarginTop();
if (!is_numeric($marginTop) || is_string($marginTop)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getMarginTop()',
'expected' => 'float',
'actual' => gettype($marginTop)
]));
]
)
);
}
$marginBottom = $this->getMarginBottom();
if (!is_numeric($marginBottom) || is_string($marginBottom)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getMarginBottom()',
'expected' => 'float',
'actual' => gettype($marginBottom)
]));
]
)
);
}
$marginLeft = $this->getMarginLeft();
if (!is_numeric($marginLeft) || is_string($marginLeft)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getMarginLeft()',
'expected' => 'float',
'actual' => gettype($marginLeft)
]));
]
)
);
}
$marginRight = $this->getMarginRight();
if (!is_numeric($marginRight) || is_string($marginRight)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getMarginRight()',
'expected' => 'float',
'actual' => gettype($marginRight)
]));
]
)
);
}
}
private function validateSupport() : void {
private function validateSupport() : void
{
$support1D = $this->getSupport1DBarcode();
if (!is_bool($support1D)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getSupport1DBarcode()',
'expected' => 'boolean',
'actual' => gettype($support1D)
]));
]
)
);
}
$support2D = $this->getSupport2DBarcode();
if (!is_bool($support2D)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getSupport2DBarcode()',
'expected' => 'boolean',
'actual' => gettype($support2D)
]));
]
)
);
}
$supportFields = $this->getSupportFields();
if (!is_int($supportFields)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getSupportFields()',
'expected' => 'integer',
'actual' => gettype($supportFields)
]));
]
)
);
}
$supportLogo = $this->getSupportLogo();
if (!is_bool($supportLogo)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getSupportLogo()',
'expected' => 'boolean',
'actual' => gettype($supportLogo)
]));
]
)
);
}
$supportTitle = $this->getSupportTitle();
if (!is_bool($supportTitle)) {
throw new \UnexpectedValueException(trans('admin/labels/message.invalid_return_type', [
throw new \UnexpectedValueException(
trans(
'admin/labels/message.invalid_return_type', [
'name' => 'getSupportTitle()',
'expected' => 'boolean',
'actual' => gettype($supportTitle)
]));
]
)
);
}
}
@@ -550,12 +638,15 @@ abstract class Label
*
* @return object (object)[ 'width' => (float)123.4, 'height' => (float)123.4 ]
*/
public static function fromFormat($format, $orientation='L', $unit='mm', $round=false) {
public static function fromFormat($format, $orientation='L', $unit='mm', $round=false)
{
$size = collect(TCPDF_STATIC::getPageSizeFromFormat(strtoupper($format)))
->sort()
->map(function ($value) use ($unit) {
->map(
function ($value) use ($unit) {
return Helper::convertUnit($value, 'pt', $unit);
})
}
)
->toArray();
$width = ($orientation == 'L') ? $size[1] : $size[0];
$height = ($orientation == 'L') ? $size[0] : $size[1];
@@ -574,13 +665,16 @@ abstract class Label
* @param string|Arrayable|array|null $path Label path[s]
* @return Collection|Label|null
*/
public static function find($name=null) {
public static function find($name=null)
{
// Find many
if (is_array($name) || $name instanceof Arrayable) {
$labels = collect($name)
->map(function ($thisname) {
->map(
function ($thisname) {
return static::find($thisname);
})
}
)
->whereNotNull();
return ($labels->count() > 0) ? $labels : null;
}
@@ -588,26 +682,36 @@ abstract class Label
// Find one
if ($name !== null) {
return static::find()
->sole(function ($label) use ($name) {
->sole(
function ($label) use ($name) {
return $label->getName() == $name;
});
}
);
}
// Find all
return collect(File::allFiles(__DIR__))
->map(function ($file) {
->map(
function ($file) {
preg_match_all('/\/*(.+?)(?:\/|\.)/', $file->getRelativePathName(), $matches);
return __NAMESPACE__ . '\\' . implode('\\', $matches[1]);
})
->filter(function ($name) {
if (!class_exists($name)) return false;
}
)
->filter(
function ($name) {
if (!class_exists($name)) { return false;
}
$refClass = new \ReflectionClass($name);
if ($refClass->isAbstract()) return false;
if ($refClass->isAbstract()) { return false;
}
return $refClass->isSubclassOf(Label::class);
})
->map(function ($name) {
}
)
->map(
function ($name) {
return new $name();
});
}
);
}

View File

@@ -33,9 +33,13 @@ abstract class RectangleSheet extends Sheet
public abstract function getLabelRowSpacing();
public function getLabelsPerPage() { return $this->getColumns() * $this->getRows(); }
public function getLabelsPerPage()
{
return $this->getColumns() * $this->getRows();
}
public function getLabelPosition($index) {
public function getLabelPosition($index)
{
$printIndex = $index + $this->getLabelIndexOffset();
$row = (int)($printIndex / $this->getColumns());
$col = $printIndex - ($row * $this->getColumns());

View File

@@ -6,12 +6,30 @@ abstract class Sheet extends Label
{
protected int $indexOffset = 0;
public function getWidth() { return $this->getPageWidth(); }
public function getHeight() { return $this->getPageHeight(); }
public function getMarginTop() { return $this->getPageMarginTop(); }
public function getMarginBottom() { return $this->getPageMarginBottom(); }
public function getMarginLeft() { return $this->getPageMarginLeft(); }
public function getMarginRight() { return $this->getPageMarginRight(); }
public function getWidth()
{
return $this->getPageWidth();
}
public function getHeight()
{
return $this->getPageHeight();
}
public function getMarginTop()
{
return $this->getPageMarginTop();
}
public function getMarginBottom()
{
return $this->getPageMarginBottom();
}
public function getMarginLeft()
{
return $this->getPageMarginLeft();
}
public function getMarginRight()
{
return $this->getPageMarginRight();
}
/**
* Returns the page width in getUnit() units
@@ -126,7 +144,8 @@ abstract class Sheet extends Label
* @param TCPDF $pdf The TCPDF instance
* @param Collection $data The data
*/
public function writeAll($pdf, $data) {
public function writeAll($pdf, $data)
{
$prevPageNumber = -1;
foreach ($data->toArray() as $recordIndex => $record) {
@@ -170,7 +189,8 @@ abstract class Sheet extends Label
*
* @return string
*/
public final function getLabelOrientation() {
public final function getLabelOrientation()
{
return ($this->getLabelWidth() >= $this->getLabelHeight()) ? 'L' : 'P';
}
@@ -179,7 +199,8 @@ abstract class Sheet extends Label
*
* @return object [ 'x1'=>0.00, 'y1'=>0.00, 'x2'=>0.00, 'y2'=>0.00, 'w'=>0.00, 'h'=>0.00 ]
*/
public final function getLabelPrintableArea() {
public final function getLabelPrintableArea()
{
return (object)[
'x1' => $this->getLabelMarginLeft(),
'y1' => $this->getLabelMarginTop(),
@@ -195,15 +216,20 @@ abstract class Sheet extends Label
*
* @return int
*/
public function getLabelIndexOffset() { return $this->indexOffset; }
public function getLabelIndexOffset()
{
return $this->indexOffset;
}
/**
* Sets label index offset (skip positions)
*
* @param int $offset
*
*/
public function setLabelIndexOffset(int $offset) { $this->indexOffset = $offset; }
public function setLabelIndexOffset(int $offset)
{
$this->indexOffset = $offset;
}
}
?>

View File

@@ -31,7 +31,8 @@ abstract class L7162 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
public function __construct() {
public function __construct()
{
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 0);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class L7162 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginTop; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginLeft; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginTop;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginLeft;
}
public function getColumns() { return 2; }
public function getRows() { return 8; }
public function getColumns()
{
return 2;
}
public function getRows()
{
return 8;
}
public function getLabelColumnSpacing() { return $this->columnSpacing; }
public function getLabelRowSpacing() { return $this->rowSpacing; }
public function getLabelColumnSpacing()
{
return $this->columnSpacing;
}
public function getLabelRowSpacing()
{
return $this->rowSpacing;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelBorder() { return 0; }
public function getLabelBorder()
{
return 0;
}
}
?>

View File

@@ -14,23 +14,59 @@ class L7162_A extends L7162
private const FIELD_SIZE = 4.60;
private const FIELD_MARGIN = 0.30;
public function getUnit() { return 'mm'; }
public function getUnit()
{
return 'mm';
}
public function getLabelMarginTop() { return 1.0; }
public function getLabelMarginBottom() { return 1.0; }
public function getLabelMarginLeft() { return 1.0; }
public function getLabelMarginRight() { return 1.0; }
public function getLabelMarginTop()
{
return 1.0;
}
public function getLabelMarginBottom()
{
return 1.0;
}
public function getLabelMarginLeft()
{
return 1.0;
}
public function getLabelMarginRight()
{
return 1.0;
}
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 4; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 4;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;

View File

@@ -17,23 +17,59 @@ class L7162_B extends L7162
private const FIELD_SIZE = 4.20;
private const FIELD_MARGIN = 0.30;
public function getUnit() { return 'mm'; }
public function getUnit()
{
return 'mm';
}
public function getLabelMarginTop() { return 1.0; }
public function getLabelMarginBottom() { return 0; }
public function getLabelMarginLeft() { return 1.0; }
public function getLabelMarginRight() { return 1.0; }
public function getLabelMarginTop()
{
return 1.0;
}
public function getLabelMarginBottom()
{
return 0;
}
public function getLabelMarginLeft()
{
return 1.0;
}
public function getLabelMarginRight()
{
return 1.0;
}
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return false; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return true; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return true;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;

View File

@@ -31,7 +31,8 @@ abstract class L7163 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
public function __construct() {
public function __construct()
{
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 0);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class L7163 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginTop; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginLeft; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginTop;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginLeft;
}
public function getColumns() { return 2; }
public function getRows() { return 7; }
public function getColumns()
{
return 2;
}
public function getRows()
{
return 7;
}
public function getLabelColumnSpacing() { return $this->columnSpacing; }
public function getLabelRowSpacing() { return $this->rowSpacing; }
public function getLabelColumnSpacing()
{
return $this->columnSpacing;
}
public function getLabelRowSpacing()
{
return $this->rowSpacing;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelBorder() { return 0; }
public function getLabelBorder()
{
return 0;
}
}
?>

View File

@@ -14,23 +14,59 @@ class L7163_A extends L7163
private const FIELD_SIZE = 4.80;
private const FIELD_MARGIN = 0.30;
public function getUnit() { return 'mm'; }
public function getUnit()
{
return 'mm';
}
public function getLabelMarginTop() { return 1.0; }
public function getLabelMarginBottom() { return 1.0; }
public function getLabelMarginLeft() { return 1.0; }
public function getLabelMarginRight() { return 1.0; }
public function getLabelMarginTop()
{
return 1.0;
}
public function getLabelMarginBottom()
{
return 1.0;
}
public function getLabelMarginLeft()
{
return 1.0;
}
public function getLabelMarginRight()
{
return 1.0;
}
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 4; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 4;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$usableWidth = $pa->w;

View File

@@ -31,7 +31,8 @@ abstract class _3490 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
public function __construct() {
public function __construct()
{
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _3490 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginTop; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginLeft; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginTop;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginLeft;
}
public function getColumns() { return 3; }
public function getRows() { return 10; }
public function getColumns()
{
return 3;
}
public function getRows()
{
return 10;
}
public function getLabelColumnSpacing() { return $this->columnSpacing; }
public function getLabelRowSpacing() { return $this->rowSpacing; }
public function getLabelColumnSpacing()
{
return $this->columnSpacing;
}
public function getLabelRowSpacing()
{
return $this->rowSpacing;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelBorder() { return 0; }
public function getLabelBorder()
{
return 0;
}
}
?>

View File

@@ -14,23 +14,59 @@ class _3490_A extends _3490
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
public function getUnit() { return 'in'; }
public function getUnit()
{
return 'in';
}
public function getLabelMarginTop() { return 0.06; }
public function getLabelMarginBottom() { return 0.06; }
public function getLabelMarginLeft() { return 0.06; }
public function getLabelMarginRight() { return 0.06; }
public function getLabelMarginTop()
{
return 0.06;
}
public function getLabelMarginBottom()
{
return 0.06;
}
public function getLabelMarginLeft()
{
return 0.06;
}
public function getLabelMarginRight()
{
return 0.06;
}
public function getSupportAssetTag() { return false; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;

View File

@@ -31,7 +31,8 @@ abstract class _5267 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
public function __construct() {
public function __construct()
{
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _5267 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginTop; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginLeft; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginTop;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginLeft;
}
public function getColumns() { return 4; }
public function getRows() { return 20; }
public function getColumns()
{
return 4;
}
public function getRows()
{
return 20;
}
public function getLabelColumnSpacing() { return $this->columnSpacing; }
public function getLabelRowSpacing() { return $this->rowSpacing; }
public function getLabelColumnSpacing()
{
return $this->columnSpacing;
}
public function getLabelRowSpacing()
{
return $this->rowSpacing;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelBorder() { return 0; }
public function getLabelBorder()
{
return 0;
}
}
?>

View File

@@ -12,23 +12,59 @@ class _5267_A extends _5267
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
public function getUnit() { return 'in'; }
public function getUnit()
{
return 'in';
}
public function getLabelMarginTop() { return 0.02; }
public function getLabelMarginBottom() { return 0.00; }
public function getLabelMarginLeft() { return 0.04; }
public function getLabelMarginRight() { return 0.04; }
public function getLabelMarginTop()
{
return 0.02;
}
public function getLabelMarginBottom()
{
return 0.00;
}
public function getLabelMarginLeft()
{
return 0.04;
}
public function getLabelMarginRight()
{
return 0.04;
}
public function getSupportAssetTag() { return false; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return false; }
public function getSupportFields() { return 1; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 1;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
if ($record->has('barcode1d')) {

View File

@@ -31,7 +31,8 @@ abstract class _5520 extends RectangleSheet
private float $labelWidth;
private float $labelHeight;
public function __construct() {
public function __construct()
{
$paperSize = static::fromFormat(self::PAPER_FORMAT, self::PAPER_ORIENTATION, $this->getUnit(), 2);
$this->pageWidth = $paperSize->width;
$this->pageHeight = $paperSize->height;
@@ -48,24 +49,63 @@ abstract class _5520 extends RectangleSheet
$this->labelHeight = Helper::convertUnit(self::LABEL_H, 'pt', $this->getUnit());
}
public function getPageWidth() { return $this->pageWidth; }
public function getPageHeight() { return $this->pageHeight; }
public function getPageWidth()
{
return $this->pageWidth;
}
public function getPageHeight()
{
return $this->pageHeight;
}
public function getPageMarginTop() { return $this->pageMarginTop; }
public function getPageMarginBottom() { return $this->pageMarginTop; }
public function getPageMarginLeft() { return $this->pageMarginLeft; }
public function getPageMarginRight() { return $this->pageMarginLeft; }
public function getPageMarginTop()
{
return $this->pageMarginTop;
}
public function getPageMarginBottom()
{
return $this->pageMarginTop;
}
public function getPageMarginLeft()
{
return $this->pageMarginLeft;
}
public function getPageMarginRight()
{
return $this->pageMarginLeft;
}
public function getColumns() { return 3; }
public function getRows() { return 10; }
public function getColumns()
{
return 3;
}
public function getRows()
{
return 10;
}
public function getLabelColumnSpacing() { return $this->columnSpacing; }
public function getLabelRowSpacing() { return $this->rowSpacing; }
public function getLabelColumnSpacing()
{
return $this->columnSpacing;
}
public function getLabelRowSpacing()
{
return $this->rowSpacing;
}
public function getLabelWidth() { return $this->labelWidth; }
public function getLabelHeight() { return $this->labelHeight; }
public function getLabelWidth()
{
return $this->labelWidth;
}
public function getLabelHeight()
{
return $this->labelHeight;
}
public function getLabelBorder() { return 0; }
public function getLabelBorder()
{
return 0;
}
}
?>

View File

@@ -14,23 +14,59 @@ class _5520_A extends _5520
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
public function getUnit() { return 'in'; }
public function getUnit()
{
return 'in';
}
public function getLabelMarginTop() { return 0.06; }
public function getLabelMarginBottom() { return 0.06; }
public function getLabelMarginLeft() { return 0.06; }
public function getLabelMarginRight() { return 0.06; }
public function getLabelMarginTop()
{
return 0.06;
}
public function getLabelMarginBottom()
{
return 0.06;
}
public function getLabelMarginLeft()
{
return 0.06;
}
public function getLabelMarginRight()
{
return 0.06;
}
public function getSupportAssetTag() { return false; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;

View File

@@ -15,23 +15,59 @@ class _5520_B extends _5520
private const FIELD_SIZE = 0.150;
private const FIELD_MARGIN = 0.012;
public function getUnit() { return 'in'; }
public function getUnit()
{
return 'in';
}
public function getLabelMarginTop() { return 0.06; }
public function getLabelMarginBottom() { return 0.06; }
public function getLabelMarginLeft() { return 0.06; }
public function getLabelMarginRight() { return 0.06; }
public function getLabelMarginTop()
{
return 0.06;
}
public function getLabelMarginBottom()
{
return 0.06;
}
public function getLabelMarginLeft()
{
return 0.06;
}
public function getLabelMarginRight()
{
return 0.06;
}
public function getSupportAssetTag() { return false; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return false; }
public function getSupportFields() { return 2; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getSupportAssetTag()
{
return false;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 2;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getLabelPrintableArea();
$currentX = $pa->x1;

View File

@@ -11,9 +11,24 @@ abstract class TZe_12mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getHeight()
{
return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
}
public function getMarginTop()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginBottom()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginLeft()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
public function getMarginRight()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
}

View File

@@ -8,18 +8,45 @@ class TZe_12mm_A extends TZe_12mm
private const BARCODE_MARGIN = 0.30;
private const TEXT_SIZE_MOD = 1.00;
public function getUnit() { return 'mm'; }
public function getWidth() { return 50.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return false; }
public function getSupportFields() { return 1; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return false; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 50.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 1;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return false;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
if ($record->has('barcode1d')) {

View File

@@ -11,9 +11,24 @@ abstract class TZe_18mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getHeight()
{
return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
}
public function getMarginTop()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginBottom()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginLeft()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
public function getMarginRight()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
}

View File

@@ -8,18 +8,45 @@ class TZe_18mm_A extends TZe_18mm
private const BARCODE_MARGIN = 0.30;
private const TEXT_SIZE_MOD = 1.00;
public function getUnit() { return 'mm'; }
public function getWidth() { return 50.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return false; }
public function getSupportFields() { return 1; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return false; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 50.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return false;
}
public function getSupportFields()
{
return 1;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return false;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
if ($record->has('barcode1d')) {

View File

@@ -11,9 +11,24 @@ abstract class TZe_24mm extends Label
private const MARGIN_SIDES = 3.20;
private const MARGIN_ENDS = 3.20;
public function getHeight() { return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit()); }
public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getHeight()
{
return Helper::convertUnit(self::HEIGHT, 'mm', $this->getUnit());
}
public function getMarginTop()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginBottom()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginLeft()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
public function getMarginRight()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
}

View File

@@ -13,18 +13,45 @@ class TZe_24mm_A extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
public function getUnit() { return 'mm'; }
public function getWidth() { return 65.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 65.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;

View File

@@ -15,18 +15,45 @@ class TZe_24mm_B extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
public function getUnit() { return 'mm'; }
public function getWidth() { return 73.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return true; }
public function getSupportTitle() { return true; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 73.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return true;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;

View File

@@ -15,18 +15,45 @@ class TZe_24mm_C extends TZe_24mm
private const FIELD_SIZE = 3.20;
private const FIELD_MARGIN = 0.15;
public function getUnit() { return 'mm'; }
public function getWidth() { return 34.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return false; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 0; }
public function getSupportLogo() { return true; }
public function getSupportTitle() { return false; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 34.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return false;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 0;
}
public function getSupportLogo()
{
return true;
}
public function getSupportTitle()
{
return false;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;

View File

@@ -14,18 +14,45 @@ class TZe_24mm_D extends TZe_24mm
private const FIELD_MARGIN = 0.35;
private const BARCODE1D_SIZE = 3.00; // Size for the C128 barcode at bottom
public function getUnit() { return 'mm'; }
public function getWidth() { return 65.0; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 3; }
public function getSupportLogo() { return false; }
public function getSupportTitle() { return true; }
public function getUnit()
{
return 'mm';
}
public function getWidth()
{
return 65.0;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 3;
}
public function getSupportLogo()
{
return false;
}
public function getSupportTitle()
{
return true;
}
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;

View File

@@ -14,10 +14,28 @@ abstract class TZe_62mm_Landscape extends Label
private const MARGIN_SIDES = 1.50;
private const MARGIN_ENDS = 1.50;
public function getWidth() { return Helper::convertUnit(self::WIDTH, 'mm', $this->getUnit()); }
public function getMarginTop() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit()); }
public function getMarginBottom() { return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());}
public function getMarginLeft() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getMarginRight() { return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit()); }
public function getRotation() { return 90; }
public function getWidth()
{
return Helper::convertUnit(self::WIDTH, 'mm', $this->getUnit());
}
public function getMarginTop()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginBottom()
{
return Helper::convertUnit(self::MARGIN_SIDES, 'mm', $this->getUnit());
}
public function getMarginLeft()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
public function getMarginRight()
{
return Helper::convertUnit(self::MARGIN_ENDS, 'mm', $this->getUnit());
}
public function getRotation()
{
return 90;
}
}

View File

@@ -4,14 +4,38 @@ namespace App\Models\Labels\Tapes\Brother;
class TZe_62mm_Landscape_A extends TZe_62mm_Landscape
{
public function getUnit() { return 'mm'; }
public function getHeight() { return 31.50; }
public function getSupportAssetTag() { return true; }
public function getSupport1DBarcode() { return true; }
public function getSupport2DBarcode() { return true; }
public function getSupportFields() { return 2; }
public function getSupportLogo() { return true; }
public function getSupportTitle() { return true; }
public function getUnit()
{
return 'mm';
}
public function getHeight()
{
return 31.50;
}
public function getSupportAssetTag()
{
return true;
}
public function getSupport1DBarcode()
{
return true;
}
public function getSupport2DBarcode()
{
return true;
}
public function getSupportFields()
{
return 2;
}
public function getSupportLogo()
{
return true;
}
public function getSupportTitle()
{
return true;
}
private const BARCODE1D_HEIGHT = 3.00;
private const BARCODE1D_MARGIN = 3.00;
@@ -27,9 +51,12 @@ class TZe_62mm_Landscape_A extends TZe_62mm_Landscape
private const FIELD_SIZE = 3.00;
private const FIELD_MARGIN = 0.10;
public function preparePDF($pdf) {}
public function preparePDF($pdf)
{
}
public function write($pdf, $record) {
public function write($pdf, $record)
{
$pa = $this->getPrintableArea();
$currentX = $pa->x1;

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