Compare commits

...

848 Commits

Author SHA1 Message Date
snipe 6f233bc218 Get more of the select2 ajax lists working 2025-11-03 15:38:43 +00:00
snipe 448f4b94af Remove classes 2025-11-03 14:03:31 +00:00
snipe 7c58fb93a9 Fixed test 2025-11-01 02:53:47 +00:00
snipe 80e4e4a40e Updated more form fields 2025-11-01 02:45:59 +00:00
snipe 7161d56ae2 Prod assets for some reason 2025-10-31 19:07:17 +00:00
snipe 1e85c7b575 Put the form-control-static back since it didn’t cut down the number of files :( 2025-10-31 19:05:36 +00:00
snipe 03130f0947 Try to undo the asset stuff for the PR 2025-10-31 19:03:50 +00:00
snipe 4812e25e5c Added name awareness in child elements 2025-10-31 19:03:19 +00:00
snipe e6c49da11c Use new blade for textarea 2025-10-31 19:02:54 +00:00
snipe 6f3ee1914a Updated maintenances screen 2025-10-31 19:02:39 +00:00
snipe 9232ee781e Updated validation JS for select2 2025-10-31 19:02:25 +00:00
snipe 3d675d375c Re-add the form static so it’s less noisy for this PR 2025-10-31 19:02:02 +00:00
snipe d2edc77197 Debugging - remove later 2025-10-31 19:01:42 +00:00
snipe 53716cbe90 Still not working (pre-selecting the ID of the asset) 2025-10-31 19:00:55 +00:00
snipe 8ce3fe0df0 Pass name to input 2025-10-31 16:21:41 +00:00
snipe affee0a990 Handle unfilled data 2025-10-31 16:21:22 +00:00
snipe 6d5f515f48 Make select2 ajax a little more flexible 2025-10-31 12:23:08 +00:00
snipe d96844498f This doesn’t actually work yet 2025-10-31 12:22:56 +00:00
snipe 08609d4f56 Moar refactorings 2025-10-30 22:26:21 +00:00
snipe be344c440f Different approach - rely on nested slots 2025-10-30 21:30:10 +00:00
snipe 96cc2c9ee9 Move form into its own component 2025-10-30 16:35:15 +00:00
snipe fb1a89442c Removed p-static class 2025-10-30 14:10:06 +00:00
snipe 359e0197bf Start building out the box+form stuff 2025-10-30 14:02:33 +00:00
snipe 6dbb836a01 Merge pull request #18125 from grokability/form-row-again
Added form row component
2025-10-29 16:33:15 +00:00
snipe 3426afe5a8 Account for input styles 2025-10-29 16:15:22 +00:00
snipe 4bbf923eb6 Smaller default row for textarea 2025-10-29 16:11:56 +00:00
snipe e2c3480194 Apply form rows to manufacturers 2025-10-29 16:11:47 +00:00
snipe 73159076f6 Better handle input groups in js validator 2025-10-29 16:11:37 +00:00
snipe 90d040573d Added regular link icon 2025-10-29 16:07:16 +00:00
snipe 155481a442 Merge pull request #18120 from grokability/small-form-fixes-settings
Fixed form label alignments in settings section
2025-10-29 11:16:59 +00:00
snipe 2f31bfc5fe Fixed some HTML labels in settings 2025-10-29 11:13:07 +00:00
snipe 8d0c88dc74 Merge pull request #18116 from akemidx/auditwarningthreshold
Fixed #17329 Audit Warning Threshold could be negative
2025-10-28 20:44:58 +00:00
snipe 07256fd833 Merge pull request #18044 from mohammad-ahmadi1/ISSUE-17004-fix-db-dump-ssl-issue
fix: update mysqldump options to use --ssl-mode=DISABLED for modern versions
2025-10-28 20:37:08 +00:00
snipe 776cd43a58 Change text string for item (versus asset) 2025-10-28 20:36:05 +00:00
akemidx acb5309aab min = 0 2025-10-28 16:32:21 -04:00
snipe c68f81db3c Merge pull request #18113 from Godmartinz/customfieldvalue-in-importer-fix
Adds #17433  `is_null` check to import handler for custom fields
2025-10-28 20:27:22 +00:00
snipe ac8e341b37 Merge pull request #18115 from marcusmoore/fixes/rb-20449-remove-admin-from-acceptance-email
Fixed #18112 - fix consumable and license acceptances
2025-10-28 20:26:59 +00:00
snipe 36122b3966 Added BACKUP_TIME_LIMIT to env example 2025-10-28 20:26:23 +00:00
akemidx 79bcf472f0 greater than or equal to zero 2025-10-28 16:13:33 -04:00
Marcus Moore 55d86da846 Remove references to administrator in acceptance notification 2025-10-28 13:07:00 -07:00
Godfrey M e4f8c1ba3f fix custom field value conditional check 2025-10-28 12:24:35 -07:00
Godfrey M c36236b7dc add is_null acheck to import hanlder for custom fields 2025-10-28 11:37:38 -07:00
snipe 63994333d0 Merge pull request #18111 from grokability/#16914-better-ldap-sync-phrasing
Fixed #16914: better ldap sync phrasing
2025-10-28 15:51:10 +00:00
snipe da4c7d8934 One more tweak 2025-10-28 15:47:56 +00:00
snipe 186721eca0 Added breadcrumbs 2025-10-28 15:42:59 +00:00
snipe f53d939c86 Better explanation for location sync, nicer look 2025-10-28 15:42:53 +00:00
snipe 23e6909708 Merge pull request #18110 from grokability/#18107-normalize-to-strings
Fixed #18107: normalize "to" strings
2025-10-28 15:03:16 +00:00
snipe cf421fe1c1 Added user-specific “to” 2025-10-28 15:02:15 +00:00
snipe 4d80e806e4 Use “-“ instead of “to” string, added placeholders 2025-10-28 15:02:03 +00:00
snipe 60a7b7f7ff Merge pull request #18109 from grokability/audit-improvements
Limit the upcoming audit email to 30 records, added optional --with-output
2025-10-28 14:31:55 +00:00
snipe 90263eab06 Added note for —with-option flag 2025-10-28 14:20:40 +00:00
snipe 9d8f251fc4 Limit the email to 30 records, added optional --with-output 2025-10-28 14:15:15 +00:00
snipe 2b4986571c Merge pull request #18108 from uberbrady/fix_ldap_tests
Fixed - LDAP test needs to be fixed to match new behavior
2025-10-28 12:49:11 +00:00
Brady Wetherington 890d13bd52 LDAP test needs to be fixed to match new behavior 2025-10-28 12:30:30 +00:00
snipe e698e71137 Merge pull request #18100 from marcusmoore/fixes/20318-license-seat-display-name
Added null safe operator in case of missing license
2025-10-28 09:29:25 +00:00
snipe d064a5530a Merge remote-tracking branch 'origin/master' into develop 2025-10-28 02:10:07 +00:00
snipe ab4fbf6c19 Merge pull request #18105 from grokability/ldap-fast-find-and-bind
Possible fix for 504 gateway timeout on unreachable LDAP server
2025-10-28 02:09:41 +00:00
snipe 728afa8361 Possible fix for 504 gateway timeout on unreachable LDAP server 2025-10-27 23:45:12 +00:00
snipe b77019c16e Merge remote-tracking branch 'origin/develop' 2025-10-27 19:32:28 +00:00
snipe 6703448b80 Merge pull request #18102 from marcusmoore/fixes/rb-20434-undefined-permissions-variable
Fixed issue when viewing user that does not have permissions set
2025-10-27 19:31:54 +00:00
Marcus Moore 776ba19a1f Define default permissions array 2025-10-27 12:28:55 -07:00
Marcus Moore 1f499e0d44 Add null safe operator in case of missing license 2025-10-27 10:47:55 -07:00
snipe 0a6eb61103 Merge remote-tracking branch 'origin/develop' 2025-10-27 13:20:18 +00:00
snipe 32a2eed5ec Fixed #18075 - make require_serial boolean in API transformer 2025-10-27 13:19:37 +00:00
snipe 40a70d39d0 Merge remote-tracking branch 'origin/develop' 2025-10-27 12:42:40 +00:00
snipe 5697054e98 Merge pull request #18099 from grokability/fix-cjk-for-acceptance-translated-strings
Fixed #18097 - check for CJK in field labels as well as content
2025-10-27 12:41:59 +00:00
snipe def5969e1c Fixed #18097 - check for CJK in field labels as well as content 2025-10-27 12:34:30 +00:00
snipe 78a418630d Merge remote-tracking branch 'origin/develop' 2025-10-27 12:21:25 +00:00
snipe 6d76e7b2d4 Added dumb pverride for reports :( 2025-10-27 12:21:16 +00:00
snipe f052c8b44c Merge pull request #18076 from uberbrady/move_traits_into_directories
Moved Traits into its directory and modify the FCO's to point to them
2025-10-27 11:43:31 +00:00
snipe 0e6991d56d Merge remote-tracking branch 'origin/develop' 2025-10-27 11:42:05 +00:00
snipe 06eab5f8a4 Merge pull request #18093 from Godmartinz/fix-warranty-part-of-expiring-asset-query
Fixed expiring warranties not being included in the expiring alerts notification
2025-10-27 11:41:51 +00:00
snipe 4a481e79c4 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/mix-manifest.json
2025-10-27 11:14:05 +00:00
snipe ee4abbcbaa Updated dev assets 2025-10-27 11:12:13 +00:00
snipe dcc82d742f Fixed RB-20430 - 500 on LDAP if baseDN is not set 2025-10-27 11:09:24 +00:00
snipe 19cb2089d7 Merge pull request #18098 from grokability/dependabot/github_actions/develop/actions/upload-artifact-5
Bump actions/upload-artifact from 4 to 5
2025-10-27 10:47:59 +00:00
snipe 04923b06b0 Merge pull request #18078 from grokability/groups-ui-improvements
Groups UI improvements, ability to add users from the group edit screen
2025-10-27 10:47:11 +00:00
dependabot[bot] e16755d491 Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 08:19:26 +00:00
snipe 742b0769a4 Use translation string 2025-10-26 12:10:08 +00:00
snipe df68dca9dc Warn if user has individual permission overrides 2025-10-25 18:57:17 +01:00
snipe 4a5bf78d58 Merge branch 'develop' into groups-ui-improvements 2025-10-25 18:31:22 +01:00
snipe 7947237489 Double check the admin status when toggling the superadmin 2025-10-25 18:29:52 +01:00
snipe 1115205164 Normalize the JS 2025-10-25 18:20:22 +01:00
snipe d5d01136c4 Fixed js errors 2025-10-25 17:57:59 +01:00
snipe 3d47277614 Added cursor style 2025-10-25 17:57:44 +01:00
snipe b937bea04f Working, but there’s a bit of a jitter I need to fix 2025-10-25 15:59:47 +01:00
snipe fff14632bc Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-10-25 14:59:31 +01:00
snipe 9bdf1a620f Built assets 2025-10-25 14:58:27 +01:00
snipe 60099aa989 FAFOing on disclosure arrows being remembered 2025-10-25 14:56:01 +01:00
snipe f3976e5dd8 Merge pull request #18094 from Godmartinz/custom_field_color_fixx
Fixed fieldset colors on dark themes
2025-10-23 20:32:05 +01:00
Godfrey M d7d6893304 fix Custom Fields section header font color 2025-10-23 12:05:13 -07:00
Godfrey M 99549ce805 rewrite query for expired warranties on assets, concat queries" 2025-10-23 11:01:51 -07:00
snipe 7eb15fe04d Merge pull request #18091 from uberbrady/fix_backup_durations
Moved import time limit inside class, added new backup time limit
2025-10-23 18:51:35 +01:00
Godfrey M e2019a13ab rewrite expired assets collection query 2025-10-23 10:49:58 -07:00
Brady Wetherington 4b7a06761a Moved import time limit inside class, added new backup time limit 2025-10-23 16:05:09 +01:00
snipe 8f4a1f5801 Moved JS and styles into js and css files 2025-10-23 13:24:26 +01:00
snipe 891bec9cdb Styling fixes 2025-10-23 13:06:45 +01:00
Godfrey M c5252ea583 skip in one more spot 2025-10-22 12:23:14 -07:00
Godfrey M 82d553c180 skip test if sqllite 2025-10-22 12:19:13 -07:00
Godfrey M 71e34355b9 remove unwanted changes 2025-10-22 11:51:00 -07:00
Godfrey M 2bad8c72e4 fixes warranty part of expiring alerts query 2025-10-22 11:43:54 -07:00
Godfrey M 6134ca01ac Merge remote-tracking branch 'origin/develop' into develop 2025-10-22 10:48:05 -07:00
snipe be8193ebff Fixed inherit permissions 2025-10-22 15:00:04 +01:00
snipe 430ee46645 Small tweaks to inherit js 2025-10-22 13:41:59 +01:00
snipe 76fbbf29e8 Replace generate text with icon button 2025-10-22 13:37:45 +01:00
snipe 3108159d95 Jitter CSS tweaks 2025-10-22 12:37:39 +01:00
snipe f282a1ead7 Removed duplicated permissions js code 2025-10-22 12:03:55 +01:00
snipe 324bc4957d Merge pull request #18080 from Godmartinz/add-null-safe-operator-to-manager
Fixes admin alerts when a location doesnt have a manager
2025-10-22 11:56:59 +01:00
snipe c3b5c4dfae Moved the logic into the permissions partial, added translations 2025-10-21 22:10:09 +01:00
Godfrey M 4ab5d97e86 add nullsafe operator to location manager 2025-10-21 14:06:08 -07:00
Godfrey M b4696ef11e added alert_email to send conditional in listener 2025-10-21 13:11:21 -07:00
snipe 294ffb72a4 Translations!!! 2025-10-21 20:36:09 +01:00
snipe 8c534d29d3 WTF tower?? 2025-10-21 19:36:28 +01:00
snipe d6cb262f9d Bumped version 2025-10-21 19:22:36 +01:00
snipe 5211e2ae20 Some UI refinement 2025-10-21 19:22:36 +01:00
snipe 90832fd1ad Checked check 2025-10-21 19:22:36 +01:00
snipe 889cbc69e1 Starter improvements - needs testing 2025-10-21 19:22:36 +01:00
snipe 15948370d4 Updated assets 2025-10-21 19:22:24 +01:00
snipe 63c5177b37 Merge pull request #18077 from Godmartinz/acceptance-notif-fixes-pt2
Adds admin to decline notification, fix asset and model name translations on asset notification
2025-10-21 18:31:28 +01:00
Godfrey M 4fbfaf6b9f add admin to decline, fix asset and model name translations 2025-10-21 10:22:34 -07:00
Brady Wetherington 9e68497b63 Moved Traits into directory and modify the users to point to them 2025-10-21 16:45:58 +01:00
snipe 0b9e13bf1e Merge remote-tracking branch 'origin/develop' 2025-10-21 12:58:06 +01:00
snipe 485d343e0f Merge pull request #18058 from uberbrady/ldap_fetch_dn_before_login_FIXED
FIXED - perform Ldap fetch for dn (Distinguished Name) before logging-in (fixed)
2025-10-21 12:56:31 +01:00
snipe 07f0e8a3be Merge pull request #18070 from Godmartinz/fix-admin-in-user-acceptance-notif
Fixes `admin` parameter acceptance notifications
2025-10-21 12:54:03 +01:00
snipe cc5afb1cd8 Merge pull request #18074 from grokability/smaller-audit-image-fix
Fixed audit images not displaying inline
2025-10-21 12:51:20 +01:00
snipe c69f1c0890 Smaller fix for missing audit images 2025-10-21 12:43:59 +01:00
Godfrey M a8733bdedf remove nullsafe operator 2025-10-20 11:01:38 -07:00
Godfrey M 4222e4eb51 add nullsafe operator after admin 2025-10-20 11:00:59 -07:00
Godfrey M 5db4441f5c fix admin param in user accepted notification 2025-10-20 10:51:24 -07:00
snipe 2bcfe97211 Merge remote-tracking branch 'origin/develop' 2025-10-20 16:03:55 +01:00
snipe 4e74c97c84 Merge pull request #18066 from smarsching/issue-18065
Fixed #18065: Ensure that private_key_bits is always an int
2025-10-20 13:51:42 +01:00
Sebastian Marsching 71a46c9bd6 Fixed #18065: ensure that private_key_bits is always an int. 2025-10-18 18:39:43 +02:00
snipe 1a7c7fdebf Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-10-17 18:37:18 +01:00
snipe 65ff3d414a Bumped version 2025-10-17 18:34:54 +01:00
snipe 9fa38b70c8 Bumped version 2025-10-17 18:26:44 +01:00
snipe 470172f53f Re-run assets 2025-10-17 18:26:05 +01:00
snipe 81261d9e36 Put the session check back in - possible fix for #18004 2025-10-17 18:25:50 +01:00
snipe 3ab2e20119 Merge remote-tracking branch 'origin/develop' 2025-10-17 17:12:47 +01:00
snipe b773d576ea Updated cipher-base library 2025-10-17 17:12:31 +01:00
snipe 23feb64b5a Fixed potential XSS on locations 2025-10-17 17:07:57 +01:00
snipe 87fe9d9d3d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/css/dist/skins/_all-skins.css
#	public/css/dist/skins/_all-skins.css.map
#	public/css/dist/skins/_all-skins.min.css
#	public/css/dist/skins/skin-contrast.css
#	public/css/dist/skins/skin-contrast.css.map
#	public/css/dist/skins/skin-contrast.min.css
#	public/mix-manifest.json
2025-10-17 17:00:10 +01:00
snipe b1ef3f51cb Merge pull request #18064 from grokability/new-fieldsets
Use new fieldset component
2025-10-17 16:58:17 +01:00
snipe e1b6488f8e Use new fieldset component 2025-10-17 16:55:36 +01:00
snipe a472dede2b Merge pull request #18063 from grokability/improved-fieldset-UI-and-better-help-test-for-ldap-mapping
New legend styles and additional help hints for LDAP
2025-10-17 15:45:05 +01:00
snipe 263cc3f7a1 New styles and additional help hints for LDAP 2025-10-17 15:29:24 +01:00
Brady Wetherington 598612d4bf Got tests to pass? Not very happy about it, tbh. 2025-10-16 19:11:49 +01:00
Brady Wetherington a07d83e583 Improved find-and-bind for complex LDAP directory structures 2025-10-16 19:11:49 +01:00
snipe d355812433 Updated assets 2025-10-16 19:06:22 +01:00
snipe 1afc14f5ab Merge remote-tracking branch 'origin/develop' 2025-10-16 19:04:30 +01:00
snipe 8947b667ae Fixed status labels skin for visited link buttons 2025-10-16 19:04:15 +01:00
snipe 6b41796d44 Fixed restore button on asset view page 2025-10-16 19:04:01 +01:00
snipe 6efe8eb55b Small fix for high contrast skin 2025-10-16 18:33:33 +01:00
snipe 4c8f8918e8 Merge remote-tracking branch 'origin/develop' 2025-10-16 15:49:59 +01:00
snipe 4cb5bb1855 Merge pull request #18057 from grokability/user-selector-fix
Fixed #18053 - user selector missing on page load without cookies
2025-10-16 15:49:39 +01:00
snipe eb007e025a Fixed #18053 - user selector missing on page load without cookies 2025-10-16 15:47:25 +01:00
snipe 18aefa9dee Merge remote-tracking branch 'origin/develop' 2025-10-16 15:10:29 +01:00
snipe 2ec540bd36 Updated language translations 2025-10-16 15:10:17 +01:00
snipe 1374ec408a Merge pull request #18019 from akemidx/emdashcharfix
Fixed #17363 - Emdash Character Encoding error
2025-10-16 15:07:31 +01:00
snipe 8f8fed2b79 Merge remote-tracking branch 'origin/develop' 2025-10-16 14:33:30 +01:00
snipe a1e6f01fe9 Fixed updating notes in bulk edit 2025-10-16 14:33:21 +01:00
snipe d7496f22e5 Merge remote-tracking branch 'origin/develop' 2025-10-16 14:04:44 +01:00
snipe 7de25a1c37 Merge pull request #18056 from grokability/add-expected-checkin-to-view-assets
Added expected_checkin to user’s View Assigned page
2025-10-16 14:04:31 +01:00
snipe 78da89340c Added expected_checkin to user’s View Assigned page 2025-10-16 13:59:59 +01:00
snipe 60606115fe Merge remote-tracking branch 'origin/develop' 2025-10-16 12:46:47 +01:00
snipe 57b49fc31c Check for array key for license report/formatters 2025-10-16 12:46:17 +01:00
snipe 6e90c8f6e6 Bumped hash for master 2025-10-16 11:28:37 +01:00
snipe a7ff2595a5 Bumped hash 2025-10-16 11:28:01 +01:00
snipe 1edea6abef Fixed RB-20347 - ambigious field in custom fields 2025-10-16 11:27:25 +01:00
snipe a3bd58bda6 Foundation 2025-10-16 11:17:48 +01:00
snipe 3339d1999f Merge remote-tracking branch 'origin/develop' 2025-10-16 11:12:56 +01:00
akemidx df3e8ec0f3 cleaning up notes 2025-10-15 17:35:25 -04:00
snipe 2dbec867d9 Merge pull request #18038 from boteroTradebe/develop
Fixed LDAP sync not syncing all user fields
2025-10-15 17:25:33 +01:00
Mohammad Ahmadi 3fc651d659 fix: update mysqldump options to use --ssl-mode=DISABLED for modern versions 2025-10-15 00:30:53 +02:00
snipe b91d23023d Fix blade template syntax for accessory fields 2025-10-14 13:45:58 +01:00
boteroTradebe cc234c60b8 Update LdapSync.php to populate/update user fields
Added missing IF statements for address, city, state, and ZIP code. Previously, this information would be pulled via LDAP sync but would not update the user data due to the missing IF statements for these attributes.
2025-10-13 13:42:11 -05:00
snipe f1883c8004 Merge remote-tracking branch 'origin/develop' 2025-10-13 14:48:46 +01:00
snipe 79f6ddc8ee Merge pull request #18036 from grokability/better-messaging-for-bulk-deletes
Fixed #18034 - Shows success message on partial bulk delete success
2025-10-13 14:30:26 +01:00
snipe 89f439a18d Shows success message on partial bulk delete success 2025-10-13 14:25:05 +01:00
snipe 79eb5bfad9 Merge pull request #18035 from grokability/fixes-bulk-error-notification
Fixed bulk error display to be more consistent and correct HTML
2025-10-13 14:02:11 +01:00
snipe 3a36b7dafd Fixed display to be more consistent and correct HTML 2025-10-13 13:57:04 +01:00
snipe 1fe2fd9891 Merge pull request #17573 from spencerrlongg/feature/8709-bulk-deletion-of-asset-categories-suppliers-manufacturers
Fixed #8709 - Bulk Deletion of Categories, Suppliers, Manufa...
2025-10-13 13:20:13 +01:00
snipe 5fdb999ece Merge branch 'develop' into feature/8709-bulk-deletion-of-asset-categories-suppliers-manufacturers 2025-10-13 13:07:53 +01:00
snipe 5a38d9c2b6 Merge pull request #18029 from grokability/dependabot/github_actions/develop/github/codeql-action-4
Bump github/codeql-action from 3 to 4
2025-10-13 12:00:49 +01:00
snipe b1c7dc6cbb Merge remote-tracking branch 'origin/develop' 2025-10-13 11:32:19 +01:00
snipe b21b2ac41c Merge pull request #18032 from grokability/move-asset-private-files
Added migration to move asset model private uploads
2025-10-13 11:31:55 +01:00
snipe 1396c597ef Move files 2025-10-13 11:19:25 +01:00
dependabot[bot] 46af72ee4e Bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 08:19:22 +00:00
snipe e3e8f553c8 Merge remote-tracking branch 'origin/develop' 2025-10-09 18:39:13 +01:00
snipe 549928d3d1 Updated assets 2025-10-09 18:38:59 +01:00
snipe abefec9628 Upgraded jdpdf to ^3.0.3 2025-10-09 18:37:59 +01:00
snipe b483eeded4 Merge remote-tracking branch 'origin/develop' 2025-10-09 11:24:47 +01:00
snipe fab85dafa8 Added employee number and email to acceptance PDF 2025-10-09 11:24:16 +01:00
snipe 955faed919 Merge pull request #18018 from grokability/advanced-user-search
Normalize advanced search
2025-10-09 11:04:28 +01:00
snipe 3ff516180d Merge remote-tracking branch 'origin/develop' 2025-10-09 10:56:08 +01:00
snipe 0c8ca6d6b0 Search on status label name 2025-10-09 10:54:11 +01:00
snipe a5de077e04 Merge pull request #17975 from marcusmoore/replace-date-and-time-display-macros
Removed date and time display format form macros
2025-10-09 04:54:05 +01:00
akemidx c0a99d6b52 notes 2025-10-08 19:20:32 -04:00
Marcus Moore a1e65cd897 Merge branch 'develop' into replace-date-and-time-display-macros
# Conflicts:
#	resources/macros/macros.php
2025-10-08 13:10:56 -07:00
snipe 5d65f1ffc5 Merge pull request #17976 from marcusmoore/17204-replace-form-digit-separator
Fixed #17204 - replace Form::digit_separator macro
2025-10-08 21:01:11 +01:00
snipe b7193a06fd Normalize advanced search 2025-10-08 20:51:35 +01:00
akemidx cba090f8eb notes 2025-10-08 15:50:00 -04:00
snipe 0d6baa1081 Merge remote-tracking branch 'origin/develop' 2025-10-08 15:29:02 +01:00
snipe bc60d796a3 Fixed RB-20329 - ambiguous clause on deleted_at 2025-10-08 15:28:48 +01:00
snipe 85a208526b Merge remote-tracking branch 'origin/develop' 2025-10-08 10:38:08 +01:00
snipe c3ac0a750d Added licenses checkin permission 2025-10-08 10:37:58 +01:00
snipe 0f111127a5 Merge pull request #18005 from marcusmoore/fixes/drop-index-migration
Fixed exception when rolling back migrations
2025-10-08 05:26:22 +01:00
snipe cd266a6bef Merge remote-tracking branch 'origin/develop' 2025-10-08 05:13:14 +01:00
snipe 0f4945621c Merge pull request #18009 from grokability/#18002-audit-image-location
Fixed #18002 - use correct path for audit images to determine validity
2025-10-08 05:12:54 +01:00
snipe 9a477f227b Fixed #18002 - use correct path for audit images to determine validity 2025-10-08 05:09:04 +01:00
akemidx 3c81257325 fix? 2025-10-07 20:23:46 -04:00
Marcus Moore 218fe9ebdc Allow Laravel to calculate index name 2025-10-07 16:04:12 -07:00
spencerrlongg 24bb45ab97 change translation 2025-10-07 13:26:08 -05:00
snipe 85f39de540 Merge pull request #18001 from grokability/#17670-fixes-advanced-search-on-assets
Fixed #17670 - advanced search on relationships not working
2025-10-07 17:35:05 +01:00
snipe 5cc3277e2d Merge pull request #18003 from uberbrady/fix_file_upload_base64
Fixed [FD-50921] - base64-encoded image files for asset creation was broken
2025-10-07 17:34:53 +01:00
snipe d5ef7f3204 Merge remote-tracking branch 'origin/develop' 2025-10-07 17:29:24 +01:00
snipe cb47d01f51 Fixed variable name in location print 2025-10-07 17:29:12 +01:00
Brady Wetherington ee499c1385 Fix base64-encoded image files for asset creation; add test 2025-10-07 16:00:16 +01:00
snipe 4ae4af5c10 Fixed #17670 - advanced search on relationships not working 2025-10-07 15:39:09 +01:00
snipe b0f5fe7e25 Merge remote-tracking branch 'origin/develop' 2025-10-07 14:32:33 +01:00
snipe fd32585efc Added notes back to audit email 2025-10-07 14:32:24 +01:00
snipe c675bb7252 Merge remote-tracking branch 'origin/develop' 2025-10-07 14:29:21 +01:00
snipe e65d11d71e Pulled incorrect button label 2025-10-07 14:29:10 +01:00
snipe c2680334f1 Clearer title on maintenances within asset context 2025-10-07 14:28:38 +01:00
snipe 1217a02ec1 Merge pull request #17992 from marcusmoore/17963-undelete-files-command
Added command to remove invalid "upload deleted" entries from the action log
2025-10-07 14:17:56 +01:00
snipe 143a091f45 Merge remote-tracking branch 'origin/develop' 2025-10-07 14:15:59 +01:00
snipe 49ecc93dbe Merge pull request #17999 from grokability/updated-audit-report
Improved upcoming audit email layout and cli feedback
2025-10-07 14:15:44 +01:00
snipe 1735fb6bed Improved audit email 2025-10-07 14:08:33 +01:00
snipe 8fefc11b4d Merge remote-tracking branch 'origin/develop' 2025-10-07 12:09:32 +01:00
snipe 1a3b22171c Merge pull request #17997 from grokability/#17924-add-url-to-maintenances
Fixed #17924 - added url to maintenances
2025-10-07 12:09:06 +01:00
snipe 5e773be260 Enhanced tests 2025-10-07 12:05:37 +01:00
snipe c317a1dc8b Added url to factory 2025-10-07 12:03:35 +01:00
snipe 9401ffc83c Added link to view 2025-10-07 11:55:32 +01:00
snipe 479a852446 Fixed #17924 - added url to maintenances 2025-10-07 11:49:10 +01:00
snipe 9188fb03f0 Merge remote-tracking branch 'origin/develop' 2025-10-07 10:49:59 +01:00
snipe 36bb91cbc3 Merge pull request #17995 from grokability/#17910-show-counts-on-mobile
Fixed #17910 - added counts to mobile view for assets
2025-10-07 10:47:41 +01:00
snipe 56e5ab8bc6 Fixed #17910 - added counts to mobile view for assets 2025-10-07 10:41:03 +01:00
snipe 607eb6ca03 Merge remote-tracking branch 'origin/develop' 2025-10-07 09:42:59 +01:00
snipe ad745cc84b Fixed #17911 - updated language string 2025-10-07 09:42:30 +01:00
Marcus Moore 5a13d5ea6f Update command description 2025-10-06 15:23:55 -07:00
Marcus Moore 0b065eb7fe Improve command 2025-10-06 15:20:43 -07:00
Marcus Moore e2c0e4bc66 WIP build out command 2025-10-06 15:14:15 -07:00
Marcus Moore 655b0e6778 Scaffold command 2025-10-06 14:59:25 -07:00
Marcus Moore cd785c9fc3 Remove name_display_format macro that was accidentally re-added 2025-10-06 13:06:52 -07:00
Marcus Moore 23885f5166 Merge branch 'develop' into 17204-replace-form-digit-separator
# Conflicts:
#	resources/macros/macros.php
2025-10-06 13:04:34 -07:00
snipe 44ef39e419 Merge remote-tracking branch 'origin/develop' 2025-10-06 20:53:14 +01:00
snipe e05a0ef565 Merge pull request #17967 from marcusmoore/fixes/17963-delete-file-via-api-fix
Fixed #17963 - over eager deletion of asset files via api
2025-10-06 20:52:57 +01:00
snipe 0ea9c0647f Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2025-10-06 15:14:56 +01:00
snipe 00b9ba2b75 Merge pull request #17989 from grokability/fixes-tcpdf-png-handling
Fixed #17940 - pngs not showing in acceptance PDFs
2025-10-06 15:13:58 +01:00
snipe f1e3bc9531 Fixed #17940 - use base64encoding on images in acceptance PDF 2025-10-06 15:02:53 +01:00
snipe 7360e093b2 Bumped version 2025-10-06 10:41:15 +01:00
snipe 5f639bc24f Updated translations 2025-10-06 10:39:04 +01:00
snipe 884d2a9552 Merge remote-tracking branch 'origin/develop' 2025-10-04 14:09:54 +01:00
snipe fd8a8b29b1 Added inactive and expired to licenses table listing 2025-10-04 13:18:51 +01:00
snipe 4e1ef40c05 Updated active button colors 2025-10-04 13:16:46 +01:00
snipe 00af0ddff5 Merge pull request #17982 from grokability/expiring-alerts-improvements
Small expiring alerts improvements
2025-10-04 01:11:03 +01:00
snipe 2467e823a5 Reordered buttons 2025-10-04 01:06:56 +01:00
snipe 224642fcb5 Removed funky search cookie 2025-10-04 01:04:25 +01:00
snipe 59518ca2c5 Small style updates 2025-10-04 00:31:17 +01:00
snipe dfb7c73069 Added column search to users 2025-10-04 00:31:06 +01:00
snipe 70eccceab3 Updated button class for add new 2025-10-04 00:18:09 +01:00
snipe 3f69b70367 Moved btn methods closer to the BS table 2025-10-03 23:54:38 +01:00
snipe 94a0a2f8be Fixed dropdown toggle 2025-10-03 23:50:40 +01:00
snipe 7e23596ab8 Translate export to CSV 2025-10-03 23:09:33 +01:00
snipe 3c58a5dd3d Fixed column selector 2025-10-03 20:36:52 +01:00
snipe 51789ccbf3 Got tooltips to work on built-in buttons! 2025-10-03 18:54:05 +01:00
snipe 6ae4a9aa1a Added tooltips to custom btn methods 2025-10-03 17:14:47 +01:00
snipe 0aebd669b2 Fixed accessor for console view 2025-10-03 16:25:31 +01:00
snipe 2a92c4899d Don’t use parenthases unless a manufacturer is given 2025-10-03 16:19:00 +01:00
snipe 530089895a Added category and manufacturer to expiring license report 2025-10-03 16:13:00 +01:00
snipe ae8289fc8c Eager load cagory and manufacturer 2025-10-03 16:12:40 +01:00
snipe 90f4dfb48b Use regular blade syntax 2025-10-03 16:03:03 +01:00
snipe ec2eddf538 Add order scope to query 2025-10-03 16:02:07 +01:00
snipe 44d31d4b39 Moved date fields closer together 2025-10-03 16:01:49 +01:00
snipe 4934e7666c Added text string 2025-10-03 16:01:38 +01:00
snipe 35bf0d020e Added breadcrumb to expiring 2025-10-03 16:01:27 +01:00
snipe 4934dc85ac Reverse expires diff direction 2025-10-03 16:00:58 +01:00
snipe 5ceb50d7e5 Added expiring licenses to API and UI 2025-10-03 16:00:23 +01:00
snipe ae7ccbb7bd Fixed icons 2025-10-03 15:59:55 +01:00
snipe 1cd9fc47aa Use diff_in_days instead 2025-10-03 15:03:14 +01:00
snipe 013c50607a Put old setter back because reasons? 2025-10-03 14:46:32 +01:00
snipe d7bf9b7f2e Added more accessors and mutators 2025-10-03 14:38:37 +01:00
snipe 4702fdddc6 Nicer output in console command 2025-10-03 14:38:08 +01:00
snipe dd06a530c0 Added Terminates string 2025-10-03 14:37:43 +01:00
snipe c36125dc95 Added CSS to the message blade 2025-10-03 14:37:25 +01:00
snipe ae43f93d0a Improved expiring assets and licenses email 2025-10-03 14:37:12 +01:00
snipe 8918b17f77 Updated test 2025-10-03 14:36:16 +01:00
snipe dfd05e8b5b Refactored scope 2025-10-03 14:13:58 +01:00
snipe 3daa6dd051 Added accessors for termination date 2025-10-03 14:13:28 +01:00
snipe 14e43192e6 Merge remote-tracking branch 'origin/develop' 2025-10-03 09:17:35 +01:00
snipe 6cf88b1792 Merge pull request #17978 from marcusmoore/17205-replace-form-email-format
Fixed #17205 - replace Form:: email_format
2025-10-03 09:15:49 +01:00
snipe 6b9839367f Merge pull request #17973 from marcusmoore/fixes/17972-update-last-checkin-upon-edit
Fixed #17972 - set last_checkin if asset is checked in during an update
2025-10-03 09:15:35 +01:00
snipe 34fcf5d616 Merge pull request #17974 from marcusmoore/replace-form-checkbox
Replaced Form::checkbox with raw html
2025-10-03 09:15:12 +01:00
snipe 1cf3c74e67 Merge pull request #17979 from akemidx/term-date-on-license-report
Fixed #17977: Term date on license report
2025-10-03 09:14:07 +01:00
snipe 16b57b931e Merge pull request #17980 from marcusmoore/17206-replace-name-display-format-macro
Fixed #17206 - replace Form::name_display_format macro
2025-10-03 09:13:23 +01:00
Marcus Moore 3457e7d617 Remove Form::name_display_format macro 2025-10-02 16:08:37 -07:00
Marcus Moore edbe8001e6 Replace Form::name_display_format 2025-10-02 16:08:16 -07:00
akemidx 71644c1cbe added term date 2025-10-02 19:00:18 -04:00
Marcus Moore 03fd8df8bd Remove Form::email_format 2025-10-02 15:52:09 -07:00
Marcus Moore 71d622b6dd Replace Form:: email_format 2025-10-02 15:51:41 -07:00
Marcus Moore 689d5a2d58 Remove digit_separator form macro 2025-10-02 15:39:28 -07:00
Marcus Moore b2e9eb866c Replace digit_separator form macro
Fixes #17204
2025-10-02 15:38:59 -07:00
Marcus Moore c8b7782d1d Remove date_display_format and time_display_format macros 2025-10-02 15:27:20 -07:00
Marcus Moore 673f936689 Replace Form:: time_display_format on localization screen 2025-10-02 15:26:31 -07:00
Marcus Moore 2ca0d39e51 Replace Form:: date_display_format on localization screen 2025-10-02 15:21:51 -07:00
Marcus Moore 908c8bc397 Remove Form::checkbox on user create screen 2025-10-02 14:33:48 -07:00
Marcus Moore 93082e1e87 Set last_checkin if asset checked in during update 2025-10-02 13:56:42 -07:00
Marcus Moore ef0a6aa25e Add failing condition 2025-10-02 13:53:19 -07:00
spencerrlongg b9f4dc1e9d translation 2025-10-02 12:00:52 -05:00
snipe 90afec864e Fixed info text help block class 2025-10-02 08:07:54 +01:00
akemidx b082fb6692 investigation and notes 2025-10-01 18:21:25 -04:00
Marcus Moore 4bbbd786cd Constrain to "uploaded" action_type 2025-10-01 14:37:36 -07:00
snipe 0c1b2a54e7 Merge remote-tracking branch 'origin/develop' 2025-10-01 22:27:06 +01:00
snipe a6ded20ede Merge pull request #17966 from uberbrady/fix_filetype_validation
Cleanups and improvements to output on snipeit:restore command
2025-10-01 22:26:49 +01:00
Brady Wetherington 9b96314371 Cleanups and improvements to output on snipeit:restore command 2025-10-01 22:07:45 +01:00
snipe 85e16ecd51 Merge remote-tracking branch 'origin/develop' 2025-10-01 21:34:50 +01:00
snipe 2ac36cdfd6 Fixed alignment on create-new dropdown in header 2025-10-01 21:32:28 +01:00
snipe e40c532354 Merge remote-tracking branch 'origin/develop' 2025-10-01 12:13:08 +01:00
snipe 9404dff79c Added note about markdown 2025-10-01 12:12:56 +01:00
snipe 55b324d8d6 Merge remote-tracking branch 'origin/develop' 2025-10-01 11:49:15 +01:00
snipe 3ed2e2d79e Added link to docs for common issues in upgrading script 2025-10-01 11:49:05 +01:00
snipe 3152d9eadd Merge remote-tracking branch 'origin/develop' 2025-10-01 11:24:33 +01:00
snipe f1266ab5d6 Check if telescope tables already exist 2025-10-01 11:24:20 +01:00
snipe e70f1408aa Merge remote-tracking branch 'origin/develop' 2025-10-01 11:19:35 +01:00
snipe 664e3984e3 Merge pull request #17877 from grokability/label-cjk-fix
Fixed CJK on labels
2025-10-01 11:16:55 +01:00
snipe 665c13e238 Merge pull request #17957 from marcusmoore/fixes/17956-handle-force-deleted-model-in-asset-edit
Fixed #17956 - handle accessing deleted model during asset update
2025-10-01 11:10:11 +01:00
snipe 8a667b20c2 Merge branch 'develop' into fixes/17956-handle-force-deleted-model-in-asset-edit 2025-10-01 11:09:40 +01:00
snipe 3693241292 Merge pull request #17959 from marcusmoore/fixes/17958-handle-force-deleted-model-in-bulk-edit
Fixes #17958 - handle accessing deleted model during bulk asset update
2025-10-01 11:06:32 +01:00
akemidx a384245368 investigation and notes 2025-09-30 16:33:33 -04:00
Marcus Moore 3c3acff79b Fix more attempted access of deleted model 2025-09-30 12:22:26 -07:00
Marcus Moore e15de83a95 Fix attempted access of deleted model 2025-09-30 12:19:12 -07:00
Marcus Moore 636fccbf97 Add failing test 2025-09-30 12:18:51 -07:00
Marcus Moore 7d8ed399a8 Fix accessing force deleted model 2025-09-30 11:27:56 -07:00
Marcus Moore 272385db6c Add failing test 2025-09-30 11:27:38 -07:00
snipe 9334b8df47 Improve Bug Report template details
Updated version placeholders and descriptions for clarity.
2025-09-30 11:57:06 +01:00
snipe e0bc2ae86f Add PHP and Composer version fields to bug report 2025-09-30 11:45:55 +01:00
snipe 2f019bb033 Merge remote-tracking branch 'origin/develop' 2025-09-30 11:40:34 +01:00
snipe 291be64aa0 Refined remnaining asset count for archived 2025-09-30 11:40:21 +01:00
snipe 75c83236ff Merge remote-tracking branch 'origin/develop' 2025-09-30 11:20:44 +01:00
snipe 72be171917 Added archived to model view 2025-09-30 11:20:33 +01:00
snipe 4ddee4ac40 Merge remote-tracking branch 'origin/develop' 2025-09-30 10:55:00 +01:00
snipe 43cd0d7eb3 Use min_amt formatter 2025-09-30 10:54:43 +01:00
snipe 6b693e2644 Merge remote-tracking branch 'origin/develop' 2025-09-30 10:51:35 +01:00
snipe eeea69d8f2 Make available_assets_count sortable 2025-09-30 10:51:25 +01:00
snipe 72cf921a4b Merge remote-tracking branch 'origin/develop' 2025-09-30 10:46:12 +01:00
spencerrlongg 32882f81e7 replace box-default 2025-09-29 15:52:43 -05:00
spencerrlongg 36f5099932 added new counts and throw new exceptions and catch them 2025-09-29 15:16:41 -05:00
snipe bec88a0441 Merge pull request #17950 from grokability/#17932-fix-remaining-counts-in-model-listing
Fixed #17932 - incorrect number for remaining assets in asset models
2025-09-29 20:55:52 +01:00
snipe 6e67e3a8a0 Fixed #17932 - incorrect number for remaining assets in asset models 2025-09-29 20:55:06 +01:00
snipe 4dc3c30354 Merge remote-tracking branch 'origin/develop' 2025-09-29 19:04:42 +01:00
snipe 947ccf911d Merge pull request #17868 from Godmartinz/adds-Tze_24mm-variant
Adds Brother Label TZe_24mm_E variant
2025-09-29 15:48:10 +01:00
snipe 06f313febe Merge pull request #17869 from marcusmoore/api-components-assigned-to-asset
Added api endpoint for retrieving components checked out to asset
2025-09-29 15:47:53 +01:00
snipe b387136b8f Merge pull request #17883 from akemidx/purchasepricereportfilter
FEATURE: Purchase Cost Report Filter
2025-09-29 15:37:39 +01:00
snipe 31614c5da1 Merge pull request #17888 from marcusmoore/fixes/bulk-checkout-extra-requests
Fixed excessive api requests on bulk checkout page
2025-09-29 14:46:19 +01:00
snipe 146b5a3085 Merge pull request #17933 from marcusmoore/17914-bulk-checkout-error-ux
Fixed #17914 - Improve UX around attempted bulk checkout of assigned assets
2025-09-29 14:44:20 +01:00
snipe 397cc1754a Merge remote-tracking branch 'origin/develop' 2025-09-29 11:05:45 +01:00
snipe ff1297cac5 Merge pull request #17945 from kingspride/develop
with --no-interactive, make composer non-interactive aswell
2025-09-29 11:04:30 +01:00
William Kirstaedter 8af3cf4056 with --no-interactive, make composer non-interactive aswell 2025-09-29 11:39:23 +02:00
Marcus Moore 9edec9e212 Extract translation 2025-09-25 11:09:27 -07:00
snipe be4362c59a Merge pull request #17925 from Godmartinz/fix-factory-auto-gen-action-logs
Adds option to disable auto generating action log from acceptance factory
2025-09-25 11:10:34 +01:00
Marcus Moore 8461b147de Link to removed assets 2025-09-24 16:38:48 -07:00
Godfrey M 82bdd43168 renamed variable 2025-09-24 15:38:30 -07:00
Godfrey M 533d82d4d8 remove unnecessary changes 2025-09-24 15:34:02 -07:00
Godfrey M 6f990dd1de adds an option to disable Auto assigned an actionlogs in factories 2025-09-24 15:27:16 -07:00
Marcus Moore be848598e3 Keep removed asset out of scope of partial 2025-09-24 14:32:25 -07:00
snipe 62a58fa23b Merge remote-tracking branch 'origin/develop' 2025-09-24 14:52:55 +01:00
snipe 7d0742054f Merge pull request #17923 from uberbrady/fix_checkout_type_selector2
Fixed #17919 - correct the behavior of the checkout type selector
2025-09-24 14:52:05 +01:00
Brady Wetherington dcf7e83507 Remove extra pointless class="active" 2025-09-24 14:47:13 +01:00
Brady Wetherington 407c2bf0c8 Switch to ?: from ?? to better handle empty strings 2025-09-24 14:45:43 +01:00
Brady Wetherington c46227ee94 Fix to the checkout-selector issue 2025-09-24 14:28:49 +01:00
snipe d8171eb056 Remove duplicate PUT route for hardware assets
Removed duplicate route definition for updating hardware assets.
2025-09-23 13:24:56 +01:00
snipe c67ca500db Clarify descriptions for multiple company support 2025-09-23 13:06:33 +01:00
snipe 0081e7b731 Revise demo reproduction question in Bug Report template 2025-09-23 13:05:26 +01:00
snipe 4e6483d3ed Enhance Bug Report template with required validations
Make fields required
2025-09-23 13:04:19 +01:00
snipe 0ddf0002c4 Change dropdown options to single quotes in Bug Report
Fixed quotes
2025-09-23 13:02:36 +01:00
snipe ad0daf33b9 Add dropdown options to Bug Report template
Added additional fields to bug report template related to FMCS
2025-09-23 13:01:37 +01:00
Marcus Moore c614c44d4c Remove assigned assets from bulk checkout 2025-09-22 16:04:29 -07:00
snipe 8a46579588 Merge pull request #17887 from marcusmoore/fixes/17404-prevent-bulk-checkout-across-companies
Fixed #17404 - Disallow bulk checkout of assets across companies
2025-09-22 18:57:43 +01:00
Marcus Moore fb9fb9c097 Merge branch 'develop' into fixes/17404-prevent-bulk-checkout-across-companies
# Conflicts:
#	app/Http/Controllers/Assets/BulkAssetsController.php
#	tests/Feature/Checkouts/Ui/BulkAssetCheckoutTest.php
2025-09-22 10:52:04 -07:00
snipe d9399534ce Merge pull request #17909 from spacjalex/17908-fix-typo
Fix #17908: typo in storage location of backups
2025-09-22 14:28:05 +01:00
spacjalex 17a749bbed fix typo 2025-09-22 15:23:14 +02:00
snipe 0b60c6a939 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-09-19 12:44:49 +01:00
Marcus Moore 2dc11a84bf Fix test name 2025-09-18 17:05:08 -07:00
Marcus Moore 2960ea15f5 Consolidate to data provider 2025-09-18 14:29:12 -07:00
Marcus Moore 17aab4c490 Implement test 2025-09-18 14:20:05 -07:00
Marcus Moore 59d0f0d292 Re-order assertions 2025-09-18 14:05:13 -07:00
Marcus Moore 27d13a113a Implement test 2025-09-18 14:01:44 -07:00
Marcus Moore c58e999fbb Scaffold tests 2025-09-18 13:11:06 -07:00
Marcus Moore a02a96d5c4 Extract translation string 2025-09-18 12:57:56 -07:00
Marcus Moore 47e9e4704d Improve error message 2025-09-18 12:56:36 -07:00
Marcus Moore b2ad9d404e Fix re-population of assets 2025-09-18 12:38:11 -07:00
snipe 925d48640d Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	config/version.php
2025-09-18 14:49:56 +01:00
snipe 028b4e7b79 Merge remote-tracking branch 'origin/develop' 2025-09-18 13:59:25 +01:00
snipe 625a46a2c2 Merge remote-tracking branch 'origin/develop' 2025-09-18 13:52:08 +01:00
snipe ebc1e27c22 Merge remote-tracking branch 'origin/develop' 2025-09-18 13:40:07 +01:00
Marcus Moore 705474dc14 Avoid pre-loading all assets on page load 2025-09-17 16:56:37 -07:00
Marcus Moore e639d7726b Disallow bulk checkout across companies 2025-09-17 14:32:27 -07:00
snipe 357e85d358 Merge remote-tracking branch 'origin/develop' 2025-09-17 22:02:11 +01:00
Marcus Moore e29b0aa6a4 Add todo 2025-09-17 13:55:54 -07:00
Marcus Moore d2157868f2 Populate failing test 2025-09-17 13:49:32 -07:00
Marcus Moore 89cfafd933 Scaffold test 2025-09-17 13:40:34 -07:00
snipe 75cfcb83aa Merge remote-tracking branch 'origin/develop' 2025-09-17 14:05:06 +01:00
snipe 63a4d1ad33 Merge remote-tracking branch 'origin/develop' 2025-09-17 11:44:41 +01:00
akemidx 69c5dbfc23 formatting 2025-09-17 05:39:45 -04:00
akemidx cf1bccfd65 prep for val 2025-09-16 15:24:44 -04:00
akemidx 99acf018f1 validation rule & query 2025-09-16 15:17:59 -04:00
snipe f04d6f37e5 Merge remote-tracking branch 'origin/develop' 2025-09-16 19:22:06 +01:00
snipe 469069b471 Merge remote-tracking branch 'origin/develop' 2025-09-16 14:33:57 +01:00
akemidx cb63c12d2f i think this is gonna need livewire to validate lol 2025-09-16 08:24:22 -04:00
akemidx 6ce0fd20ce works, needs error handling 2025-09-16 08:11:42 -04:00
snipe b383cd9493 Fixed CJK on labels 2025-09-16 12:10:01 +01:00
snipe 9bca5912d9 Merge remote-tracking branch 'origin/develop' 2025-09-16 11:39:12 +01:00
snipe 23756ba1c7 Merge remote-tracking branch 'origin/develop' 2025-09-16 11:27:27 +01:00
Marcus Moore 06f060161d Apply offset and limit 2025-09-15 15:43:54 -07:00
Marcus Moore 73e0628124 Populate test 2025-09-15 15:26:30 -07:00
Marcus Moore 7393c4170b Apply first pass and scaffold additional test 2025-09-15 15:22:35 -07:00
Marcus Moore 73e185bf9d Scaffold route and tests 2025-09-15 15:12:05 -07:00
Godfrey M 77153c3e78 adds Tze_24mm variant 2025-09-15 11:20:35 -07:00
snipe 07227887f6 Merge remote-tracking branch 'origin/develop' 2025-09-15 14:42:08 +01:00
snipe 13d3b103f1 Merge remote-tracking branch 'origin/develop' 2025-09-15 13:43:23 +01:00
snipe 4bdfd0e115 Merge remote-tracking branch 'origin/develop' 2025-09-15 10:32:08 +01:00
snipe 786b20708e Merge remote-tracking branch 'origin/develop' 2025-09-15 08:38:56 +01:00
snipe 0e957cad84 Merge remote-tracking branch 'origin/develop' 2025-09-12 18:08:15 +01:00
snipe 85c728f313 Merge remote-tracking branch 'origin/develop' 2025-09-11 11:40:26 +01:00
akemidx 50e210b2db fixing naming convention to match 2025-09-10 17:35:09 -04:00
snipe ba3fb8cd66 Merge remote-tracking branch 'origin/develop' 2025-09-10 20:19:55 +01:00
snipe d9fb7dc754 Merge remote-tracking branch 'origin/develop' 2025-09-10 15:48:31 +01:00
snipe 2249dad9d7 Merge remote-tracking branch 'origin/develop' 2025-09-10 15:43:21 +01:00
snipe 5c4fa630ae Merge remote-tracking branch 'origin/develop' 2025-09-10 15:40:24 +01:00
snipe ede74ad24e Merge remote-tracking branch 'origin/develop' 2025-09-10 15:38:30 +01:00
snipe 27542a8f91 Okay, I guess we can’t require that field :(
Signed-off-by: snipe <snipe@snipe.net>
2025-09-10 15:34:30 +01:00
snipe 5dc07b94aa Merge remote-tracking branch 'origin/develop' 2025-09-10 15:33:20 +01:00
snipe d7acf721ae Merge remote-tracking branch 'origin/develop' 2025-09-10 15:30:54 +01:00
snipe eff5232828 Merge remote-tracking branch 'origin/develop' 2025-09-10 15:26:14 +01:00
snipe 3eb29b1cdb Merge remote-tracking branch 'origin/develop' 2025-09-10 15:21:05 +01:00
snipe c9961f63b4 Merge remote-tracking branch 'origin/develop' 2025-09-10 13:53:21 +01:00
akemidx b1de98f05d first front end 2025-09-09 19:18:29 -04:00
snipe 09e843a800 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-09-09 22:34:33 +01:00
snipe 77b79dbd95 Merge remote-tracking branch 'origin/develop' 2025-09-08 15:04:42 +01:00
snipe dafc6c5136 Merge remote-tracking branch 'origin/develop' 2025-09-08 14:34:16 +01:00
snipe c790147a5c Merge remote-tracking branch 'origin/develop' 2025-09-08 13:33:29 +01:00
snipe 80c39c5ef3 Merge remote-tracking branch 'origin/develop' 2025-09-08 12:17:41 +01:00
snipe 9a3e84d84c Merge remote-tracking branch 'origin/develop' 2025-09-08 09:27:51 +01:00
snipe 643960c829 Merge remote-tracking branch 'origin/develop' 2025-09-08 08:25:08 +01:00
snipe 1737018325 Merge remote-tracking branch 'origin/develop' 2025-09-08 08:14:45 +01:00
snipe 484d5ba76e Merge remote-tracking branch 'origin/develop' 2025-09-04 17:15:53 +01:00
snipe 798685d0b8 Merge remote-tracking branch 'origin/develop' 2025-09-04 16:56:55 +01:00
snipe cb7654ae90 Merge remote-tracking branch 'origin/develop' 2025-09-04 16:55:57 +01:00
snipe 00c394345a Merge remote-tracking branch 'origin/develop' 2025-09-03 15:09:42 +01:00
snipe dffcb62fa1 Merge remote-tracking branch 'origin/develop' 2025-09-03 14:35:31 +01:00
snipe 8c668b72b7 Merge remote-tracking branch 'origin/develop' 2025-09-03 08:45:20 +01:00
snipe b54ecd4da0 Merge remote-tracking branch 'origin/develop' 2025-09-02 13:45:41 +01:00
snipe 87a7e3501b Merge remote-tracking branch 'origin/develop' 2025-09-02 11:30:44 +01:00
snipe daefec3013 Merge remote-tracking branch 'origin/develop' 2025-09-01 20:32:57 +01:00
snipe 183a9742c4 Merge remote-tracking branch 'origin/develop' 2025-09-01 17:08:43 +01:00
snipe 04b83f8176 Merge remote-tracking branch 'origin/develop' 2025-09-01 12:28:40 +01:00
snipe 335ab3f064 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-09-01 11:57:07 +01:00
snipe 7dd493da35 Merge remote-tracking branch 'origin/develop' 2025-08-29 10:17:26 +01:00
snipe 560bd6da92 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	resources/views/notifications/markdown/report-expiring-assets.blade.php
2025-08-29 10:08:56 +01:00
snipe a5824ccc5f Merge pull request #17754 from marcusmoore/fixes/name-in-expiring-assets-master
Patch #17751 to master take 2
2025-08-29 01:25:46 +01:00
Marcus Moore 830a7964a4 Use display_name in place of name() 2025-08-28 17:23:56 -07:00
snipe b1359c3277 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-08-28 18:34:14 +01:00
snipe 0ba8f5cc5a Merge remote-tracking branch 'origin/develop' 2025-08-28 18:06:29 +01:00
snipe 6fb9e2c38e Merge remote-tracking branch 'origin/develop' 2025-08-28 16:12:18 +01:00
snipe eebc2ab8be Merge remote-tracking branch 'origin/develop' 2025-08-28 07:30:15 +01:00
snipe b65b3151ee Merge remote-tracking branch 'origin/develop' 2025-08-28 05:29:48 +01:00
spencerrlongg 1d24b7985b another translation change 2025-08-27 17:25:17 -05:00
spencerrlongg 526bb2c650 more translation changes 2025-08-27 17:23:46 -05:00
spencerrlongg c450c0ddb8 lots of translation changes 2025-08-27 17:17:18 -05:00
snipe 13a0f49f5f Merge remote-tracking branch 'origin/develop' 2025-08-27 16:36:05 +01:00
snipe 199eefafa1 Merge remote-tracking branch 'origin/develop' 2025-08-27 14:38:47 +01:00
snipe c5b58f9ecc Merge remote-tracking branch 'origin/develop' 2025-08-27 13:32:25 +01:00
snipe 6b68fe4de6 Merge remote-tracking branch 'origin/develop' 2025-08-27 13:30:19 +01:00
snipe 3461bbfdb3 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-08-27 12:28:22 +01:00
spencerrlongg 51f6927076 add details block for more than 3 errors to notification 2025-08-26 21:50:08 -05:00
spencerrlongg 1d88cf443f revert SuppliersController as well 2025-08-25 18:45:31 -05:00
spencerrlongg 7b6c0c3a40 Revert "tests passing, needs some manual testing"
This reverts commit fdb0651bf4.
2025-08-25 18:42:11 -05:00
spencerrlongg fdb0651bf4 tests passing, needs some manual testing 2025-08-25 18:25:36 -05:00
spencerrlongg c39d484611 rm unused import, new prop 2025-08-25 16:30:53 -05:00
spencerrlongg c42996429f rm unused import 2025-08-25 14:10:12 -05:00
spencerrlongg a091baf5a6 component finally working 2025-08-25 14:06:56 -05:00
snipe 00a17cd55e Merge remote-tracking branch 'origin/develop' 2025-08-21 11:51:50 +01:00
spencerrlongg 643d44af22 change \Throwable to \Exceptionm, add missing report()s 2025-08-20 23:09:59 -05:00
spencerrlongg b934f43db0 rename all exceptions 2025-08-20 22:58:39 -05:00
snipe f39afe5a65 Merge remote-tracking branch 'origin/develop' 2025-08-20 15:56:19 +01:00
snipe 7612ee6b08 Merge remote-tracking branch 'origin/develop' 2025-08-20 14:17:38 +01:00
snipe 2ed2b0101a Merge remote-tracking branch 'origin/develop' 2025-08-20 12:43:57 +01:00
snipe 5ca9d31964 Merge remote-tracking branch 'origin/develop' 2025-08-20 11:32:27 +01:00
snipe 2fcd8cd261 Merge remote-tracking branch 'origin/develop' 2025-08-20 11:00:26 +01:00
snipe 0ffa47a2c6 Merge remote-tracking branch 'origin/develop' 2025-08-20 09:58:54 +01:00
snipe e203d4dee3 Merge remote-tracking branch 'origin/develop' 2025-08-19 14:49:00 +01:00
snipe b47d773e13 Merge remote-tracking branch 'origin/develop' 2025-08-19 14:34:32 +01:00
snipe a8d0a4a95d Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/mix-manifest.json
2025-08-19 14:12:58 +01:00
snipe 3fb0804cef Merge remote-tracking branch 'origin/develop' 2025-08-19 13:57:36 +01:00
snipe 6811ebcd52 Merge remote-tracking branch 'origin/develop' 2025-08-19 10:12:56 +01:00
snipe 4fe7bfb851 Merge remote-tracking branch 'origin/develop' 2025-08-18 15:24:25 +01:00
snipe fb60985d03 Merge remote-tracking branch 'origin/develop' 2025-08-18 12:47:19 +01:00
snipe 8f575923cf Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-08-18 11:26:43 +01:00
snipe 0ecfd02649 Merge remote-tracking branch 'origin/develop' 2025-08-18 11:00:30 +01:00
snipe 420aaf4f61 Merge remote-tracking branch 'origin/develop' 2025-08-18 09:45:19 +01:00
snipe 0c35f213e1 Merge remote-tracking branch 'origin/develop' 2025-08-17 14:54:43 +01:00
snipe f68813af13 Merge remote-tracking branch 'origin/develop' 2025-08-17 14:11:41 +01:00
snipe 37a90d0ce9 Merge remote-tracking branch 'origin/develop' 2025-08-15 15:07:29 +02:00
snipe 02f1291e8f Merge remote-tracking branch 'origin/develop' 2025-08-15 14:41:24 +02:00
snipe 92e4f6b5d9 Merge remote-tracking branch 'origin/develop' 2025-08-15 14:31:53 +02:00
snipe 7b7738fbcc Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-08-15 14:24:30 +02:00
snipe 31197604a3 Merge pull request #17602 from grokability/develop
Merge develop into master
2025-08-13 21:19:39 +02:00
spencerrlongg e33b1b6c90 fixed maintenances 2025-08-12 13:34:40 -05:00
Spencer Long 30520297e8 Merge branch 'develop' into feature/8709-bulk-deletion-of-asset-categories-suppliers-manufacturers 2025-08-12 11:58:11 -06:00
spencerrlongg 78ca1d1335 some cleanup 2025-08-11 21:07:08 -05:00
spencerrlongg 6159ee8c2c category done! 2025-08-11 20:16:43 -05:00
spencerrlongg 5cd5392958 manufacturer completed, just categories left 2025-08-11 19:42:09 -05:00
spencerrlongg 0dcdfc5d14 fix tests after routing change 2025-08-11 19:11:40 -05:00
spencerrlongg d0e068f1c0 suppliers completely done, rinse and repeat for the other two 2025-08-11 19:04:36 -05:00
snipe f42a2d7457 Merge remote-tracking branch 'origin/develop' 2025-08-11 20:45:38 +01:00
snipe d29619b67c Merge remote-tracking branch 'origin/develop' 2025-08-11 18:50:17 +01:00
snipe f5235cb835 Merge remote-tracking branch 'origin/develop' 2025-08-11 18:12:51 +01:00
snipe ee830e0cb4 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/app.css.map
#	public/css/build/overrides.css
#	public/css/build/overrides.css.map
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-08-11 14:58:57 +01:00
snipe 0cd3be003d Merge remote-tracking branch 'origin/develop' 2025-08-11 13:07:00 +01:00
snipe c93e35ec77 Merge remote-tracking branch 'origin/develop' 2025-08-11 11:18:31 +01:00
snipe 9538a76232 Merge remote-tracking branch 'origin/develop' 2025-08-11 06:26:29 +01:00
snipe 05876bb124 Merge remote-tracking branch 'origin/develop' 2025-08-11 05:05:13 +01:00
snipe 8bcd5a6d2a Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-08-10 21:04:55 +01:00
snipe a36afbcb25 Merge remote-tracking branch 'origin/develop' 2025-08-10 21:02:48 +01:00
snipe ebd8d085cf Merge remote-tracking branch 'origin/develop' 2025-08-10 21:01:34 +01:00
snipe 505148b024 Merge remote-tracking branch 'origin/develop' 2025-08-10 20:51:27 +01:00
spencerrlongg 3eefeec4ce partials made, need to figure out all this jquery, button disabled 2025-08-09 22:40:00 -05:00
spencerrlongg b61419c1ce oop, revert delete 2025-08-09 22:19:28 -05:00
spencerrlongg f590fcffbc some tests, a component i probably won't use, beginning of front end 2025-08-09 22:16:23 -05:00
snipe e87e924ac2 Merge remote-tracking branch 'origin/develop' 2025-08-08 11:37:46 +01:00
snipe 90f261bab6 Merge remote-tracking branch 'origin/develop' 2025-08-08 11:32:14 +01:00
snipe f7dfb09a4d Merge remote-tracking branch 'origin/develop' 2025-08-08 09:56:23 +01:00
snipe 3135917127 Merge remote-tracking branch 'origin/develop' 2025-08-07 17:03:01 +01:00
snipe 52afa3d36d Merge remote-tracking branch 'origin/develop' 2025-08-06 16:35:59 +01:00
snipe 242aa60e04 Merge remote-tracking branch 'origin/develop' 2025-08-06 16:26:14 +01:00
snipe 7a3c2c27ff Merge remote-tracking branch 'origin/develop' 2025-08-05 23:19:38 +01:00
snipe 5d124360c2 Merge remote-tracking branch 'origin/develop' 2025-08-05 19:35:12 +01:00
snipe 365d7448d5 Merge remote-tracking branch 'origin/develop' 2025-08-05 19:06:34 +01:00
snipe 9a0102c723 Merge remote-tracking branch 'origin/develop' 2025-08-05 19:03:00 +01:00
snipe 2f77f2cb2b Merge remote-tracking branch 'origin/develop' 2025-08-05 18:13:27 +01:00
snipe 528e3a2106 Merge remote-tracking branch 'origin/develop' 2025-08-05 17:48:12 +01:00
snipe 032a664d4c Merge remote-tracking branch 'origin/develop' 2025-08-04 22:27:59 +01:00
snipe aac1864c9b Merge remote-tracking branch 'origin/develop' 2025-08-04 21:23:41 +01:00
snipe e3477f3306 Merge remote-tracking branch 'origin/develop' 2025-08-02 18:41:39 +01:00
snipe 6620a4f87b Merge remote-tracking branch 'origin/develop' 2025-08-02 14:47:09 +01:00
snipe c0e9dff5bf Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/js/dist/all.js
#	public/js/dist/all.js.map
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2025-08-01 23:13:22 +01:00
snipe 2d961c435a Merge remote-tracking branch 'origin/develop' 2025-07-31 04:11:31 +01:00
snipe 7c95f03166 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/AdminLTE.css
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/css/dist/bootstrap-table.css
#	public/css/dist/skins/_all-skins.css
#	public/css/dist/skins/_all-skins.min.css
#	public/css/dist/skins/skin-black-dark.css
#	public/css/dist/skins/skin-black-dark.min.css
#	public/css/dist/skins/skin-black.css
#	public/css/dist/skins/skin-black.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-blue.css
#	public/css/dist/skins/skin-blue.min.css
#	public/css/dist/skins/skin-contrast.css
#	public/css/dist/skins/skin-contrast.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-green.css
#	public/css/dist/skins/skin-green.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-orange.css
#	public/css/dist/skins/skin-orange.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-purple.css
#	public/css/dist/skins/skin-purple.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-red.css
#	public/css/dist/skins/skin-red.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/css/dist/skins/skin-yellow.css
#	public/css/dist/skins/skin-yellow.min.css
#	public/js/build/app.js
#	public/js/build/vendor.js
#	public/js/dist/all.js
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2025-07-28 17:41:13 +01:00
snipe 31e5c13b50 Merge remote-tracking branch 'origin/develop' 2025-07-28 03:30:22 +01:00
snipe 4a9fe4f981 Merge remote-tracking branch 'origin/develop' 2025-07-24 15:54:54 +01:00
snipe 4fcc5587ee Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-07-24 15:36:06 +01:00
snipe 6ca49a20ce Merge remote-tracking branch 'origin/develop' 2025-07-24 15:29:54 +01:00
snipe 28f293fdc1 Merge remote-tracking branch 'origin/develop' 2025-07-24 13:07:09 +01:00
snipe b3e7619adc Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-07-23 16:11:20 +01:00
snipe 6e56d56137 Merge remote-tracking branch 'origin/develop' 2025-07-23 15:06:25 +01:00
snipe 2528f6a07b Merge remote-tracking branch 'origin/develop' 2025-07-23 14:56:49 +01:00
snipe 423d07c919 Merge remote-tracking branch 'origin/develop' 2025-07-23 12:41:20 +01:00
snipe cc608de4bf Merge remote-tracking branch 'origin/develop' 2025-07-23 12:28:35 +01:00
snipe f999a68608 Merge remote-tracking branch 'origin/develop' 2025-07-22 20:28:53 +01:00
snipe db78a9f18f Merge remote-tracking branch 'origin/develop' 2025-07-22 15:25:51 +01:00
snipe 816039f48e Merge remote-tracking branch 'origin/develop' 2025-07-22 15:18:50 +01:00
snipe ae240bae6d Updated prod CSS
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 15:14:42 +01:00
snipe 9e30c69e6d Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-07-22 15:14:32 +01:00
snipe 43c7de9049 Updated prod assets
Signed-off-by: snipe <snipe@snipe.net>
2025-07-22 14:38:04 +01:00
snipe 7e51c5db81 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-07-22 14:34:10 +01:00
snipe 0ee3c45e7b Merge remote-tracking branch 'origin/develop' 2025-07-22 13:39:54 +01:00
snipe 981e69929c Merge remote-tracking branch 'origin/develop' 2025-07-21 12:36:44 +01:00
snipe 1eae5d12fc Merge remote-tracking branch 'origin/develop' 2025-07-18 17:56:20 +01:00
snipe 8863208333 Merge remote-tracking branch 'origin/develop' 2025-07-16 17:35:55 +01:00
snipe 5f38a74a72 Merge remote-tracking branch 'origin/develop' 2025-07-16 17:02:29 +01:00
snipe fe15dacb1f Merge remote-tracking branch 'origin/develop' 2025-07-16 16:54:30 +01:00
snipe c2d44cf2f2 Merge remote-tracking branch 'origin/develop' 2025-07-16 12:25:14 +01:00
snipe 7f1bdb6f34 Merge remote-tracking branch 'origin/develop' 2025-07-15 10:58:58 +01:00
spencerrlongg 7cdfaa93ec a couple more tests and cleanup 2025-07-10 16:45:15 -05:00
spencerrlongg 59ccc70303 bulk changes that should make this work 2025-07-10 16:02:39 -05:00
spencerrlongg f1584b722d work on bulk tests, switching branches to check something 2025-07-10 15:29:42 -05:00
snipe b0305e12d2 Merge remote-tracking branch 'origin/develop' 2025-07-10 13:11:10 +01:00
spencerrlongg 4d8c5a86a4 routes added, tests scratched but need writing 2025-07-09 23:01:58 -05:00
snipe 58f76b5c99 Merge remote-tracking branch 'origin/develop' 2025-07-09 21:01:24 +01:00
snipe 7c4ee632cf Merge remote-tracking branch 'origin/develop' 2025-07-09 20:58:17 +01:00
snipe b6b0f716eb Merge remote-tracking branch 'origin/develop' 2025-07-09 20:22:25 +01:00
snipe bd0e04ed15 Merge remote-tracking branch 'origin/develop' 2025-07-09 15:48:17 +01:00
snipe 8599981d44 Merge remote-tracking branch 'origin/develop' 2025-07-09 15:46:53 +01:00
snipe 6fc6e95c67 Merge remote-tracking branch 'origin/develop' 2025-07-08 22:02:34 +01:00
snipe 43b585bde8 Merge remote-tracking branch 'origin/develop' 2025-07-08 21:59:58 +01:00
snipe 710f89291f Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-07-08 21:27:06 +01:00
spencerrlongg 5f835aa009 delete unused imports 2025-07-07 18:07:26 -05:00
spencerrlongg d5ca543719 start introducing parent exception 2025-07-07 18:05:23 -05:00
snipe 4c6249eb9e Merge remote-tracking branch 'origin/develop' 2025-07-07 22:09:45 +01:00
snipe 016900bad8 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-07-07 21:54:04 +01:00
snipe 2e8ae33761 Merge remote-tracking branch 'origin/develop' 2025-07-07 20:58:36 +01:00
snipe 0d325060da Merge remote-tracking branch 'origin/develop' 2025-07-07 17:06:24 +01:00
snipe 1a6e98e18f Merge remote-tracking branch 'origin/develop' 2025-07-07 16:24:04 +01:00
snipe 97e34595f6 Merge remote-tracking branch 'origin/develop' 2025-07-07 16:00:10 +01:00
snipe a179d5234b Merge remote-tracking branch 'origin/develop' 2025-07-07 14:43:28 +01:00
snipe 64c6121fdb Merge remote-tracking branch 'origin/develop' 2025-07-07 14:00:55 +01:00
snipe 08d8954a85 Merge remote-tracking branch 'origin/develop' 2025-07-07 14:00:03 +01:00
snipe 4f20955d0d Merge remote-tracking branch 'origin/develop' 2025-07-07 13:46:03 +01:00
snipe 3a703c8bcf Merge remote-tracking branch 'origin/develop' 2025-07-07 13:35:07 +01:00
snipe ccbffa086b Merge remote-tracking branch 'origin/develop' 2025-07-07 11:36:33 +01:00
snipe 07ee4be840 Merge remote-tracking branch 'origin/develop' 2025-07-02 18:38:12 +01:00
snipe 4cc9b2d312 Merge remote-tracking branch 'origin/develop' 2025-07-02 17:21:39 +01:00
snipe 24dddae1d1 Merge remote-tracking branch 'origin/develop' 2025-07-02 11:57:57 +01:00
snipe ad0165d085 Merge remote-tracking branch 'origin/develop' 2025-07-02 11:44:30 +01:00
snipe 39dc38c5d1 Merge remote-tracking branch 'origin/develop' 2025-07-01 23:32:43 +01:00
snipe 046ce19dbb Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-06-30 11:46:24 +01:00
snipe 33263f5a93 Merge remote-tracking branch 'origin/develop' 2025-06-27 13:52:09 +01:00
snipe 8ef8e76300 Merge remote-tracking branch 'origin/develop' 2025-06-27 12:48:54 +01:00
snipe 73cfdae9e7 Merge remote-tracking branch 'origin/develop' 2025-06-25 15:00:46 +01:00
snipe 54a3e41281 Merge remote-tracking branch 'origin/develop' 2025-06-25 14:58:46 +01:00
snipe d59ba6da84 Merge remote-tracking branch 'origin/develop' 2025-06-25 13:11:39 +01:00
snipe 1b28b06934 Merge remote-tracking branch 'origin/develop' 2025-06-25 11:04:15 +01:00
snipe ff3e69a56c Merge remote-tracking branch 'origin/develop' 2025-06-23 20:51:24 +01:00
snipe 6b975a5fb4 Merge remote-tracking branch 'origin/develop' 2025-06-23 16:41:42 +01:00
snipe 3a02b15124 Merge remote-tracking branch 'origin/develop' 2025-06-23 12:38:40 +01:00
snipe 5f73d81935 Merge remote-tracking branch 'origin/develop' 2025-06-22 20:14:59 +01:00
snipe 002b5c0f6f Merge remote-tracking branch 'origin/develop' 2025-06-22 20:07:52 +01:00
snipe 8086842570 Merge remote-tracking branch 'origin/develop' 2025-06-22 19:42:03 +01:00
snipe 51f2d5a664 Merge remote-tracking branch 'origin/develop' 2025-06-22 19:30:42 +01:00
spencerrlongg db63ad1cf4 add image check to supplier action 2025-06-17 18:13:37 -05:00
snipe b74b76de75 Merge remote-tracking branch 'origin/develop' 2025-06-17 18:17:10 +01:00
snipe a1b1498106 Merge remote-tracking branch 'origin/develop' 2025-06-17 17:51:37 +01:00
snipe 1158851ea7 Merge remote-tracking branch 'origin/develop' 2025-06-17 15:57:12 +01:00
snipe e1ab9e959e Merge remote-tracking branch 'origin/develop' 2025-06-17 13:03:27 +01:00
snipe 2bbac3ae9d Merge remote-tracking branch 'origin/develop' 2025-06-17 12:38:52 +01:00
snipe e889b1d5e5 Merge remote-tracking branch 'origin/develop' 2025-06-17 11:42:00 +01:00
snipe 233bf856f4 Merge remote-tracking branch 'origin/develop' 2025-06-17 11:39:12 +01:00
snipe bca843e06c Merge remote-tracking branch 'origin/develop' 2025-06-16 20:32:40 +01:00
snipe 30a79a1278 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-06-16 14:45:53 +01:00
snipe 0f92dee2c4 Merge remote-tracking branch 'origin/develop' 2025-06-15 03:41:23 +01:00
snipe f04efede15 Merge remote-tracking branch 'origin/develop' 2025-06-15 02:59:47 +01:00
snipe f0dfdf6720 Merge remote-tracking branch 'origin/develop' 2025-06-14 23:56:06 +01:00
snipe e26d731382 Merge remote-tracking branch 'origin/develop' 2025-06-14 23:08:48 +01:00
snipe d684d3e559 Merge remote-tracking branch 'origin/develop' 2025-06-14 22:22:54 +01:00
snipe 47c54cb998 Merge remote-tracking branch 'origin/develop' 2025-06-14 17:45:44 +01:00
snipe 592cb2b3ec Merge remote-tracking branch 'origin/develop' 2025-06-14 17:08:11 +01:00
snipe f5a7871a2e Merge remote-tracking branch 'origin/develop' 2025-06-14 16:44:48 +01:00
snipe ec411fa0db Merge remote-tracking branch 'origin/develop' 2025-06-14 12:17:43 +01:00
spencerrlongg 5da79cd5ca destroy manufacturer action, bulk manufacturer controller 2025-06-12 17:11:45 -05:00
snipe a850a9bb83 Merge remote-tracking branch 'origin/develop' 2025-06-12 21:34:59 +01:00
spencerrlongg 13c971b171 some refinements, bulk categories controller 2025-06-12 15:19:49 -05:00
spencerrlongg 1cee7e43ed more (reusable) exceptions, a couple notes 2025-06-12 00:47:57 -05:00
spencerrlongg 5e81c63d6e some notes and things moved 2025-06-11 11:39:14 -05:00
snipe 479b7a3f94 Merge remote-tracking branch 'origin/develop' 2025-06-11 10:32:49 +01:00
snipe f7cfee77c9 Merge remote-tracking branch 'origin/develop' 2025-06-11 10:20:22 +01:00
snipe 65a8126a13 Merge remote-tracking branch 'origin/develop' 2025-06-09 13:40:44 +01:00
snipe a39bc102d5 Merge remote-tracking branch 'origin/develop' 2025-06-08 15:30:32 +01:00
snipe 81d930c4d2 Merge remote-tracking branch 'origin/develop' 2025-06-08 15:19:19 +01:00
snipe 6839623061 Merge remote-tracking branch 'origin/develop' 2025-06-06 17:03:37 +01:00
snipe 7de2809d42 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2025-06-06 14:05:47 +01:00
snipe 98ec6b6886 Merge remote-tracking branch 'origin/develop' 2025-06-06 12:48:54 +01:00
snipe 04827f00cc Merge remote-tracking branch 'origin/develop' 2025-06-06 11:54:10 +01:00
snipe 660bfc6578 Merge remote-tracking branch 'origin/develop' 2025-06-06 08:31:09 +01:00
snipe 1152cd5537 Merge remote-tracking branch 'origin/develop' 2025-06-06 08:26:52 +01:00
spencerrlongg 17456482d6 category destroy action 2025-06-04 21:42:58 -05:00
spencerrlongg a0431e1912 supplier actions working 2025-06-04 20:44:18 -05:00
snipe f30e8497b2 Merge remote-tracking branch 'origin/develop' 2025-06-04 16:17:26 +01:00
snipe 06495bc45d Merge remote-tracking branch 'origin/develop' 2025-06-04 12:13:30 +01:00
snipe 26067916b3 Merge remote-tracking branch 'origin/develop' 2025-06-04 11:49:03 +01:00
snipe c36ee4852b Merge remote-tracking branch 'origin/develop' 2025-06-04 10:40:56 +01:00
snipe 2cb992ad44 Merge remote-tracking branch 'origin/develop' 2025-06-04 08:43:52 +01:00
spencerrlongg ee5aac8008 supplier destroy action creaated and a lot scratched out 2025-06-03 20:25:07 -05:00
snipe 083b7be6c0 Make version number match tagged version :(
Signed-off-by: snipe <snipe@snipe.net>
2025-06-03 11:03:33 +01:00
snipe e24854558f Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-06-03 11:03:05 +01:00
snipe e4314cf426 Merge remote-tracking branch 'origin/develop' 2025-06-03 06:07:20 +01:00
snipe 4106e4e45c Merge remote-tracking branch 'origin/develop' 2025-06-02 22:29:09 +01:00
snipe 05f143db2b Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-06-02 18:15:54 +01:00
snipe 64aeaeeeea Merge remote-tracking branch 'origin/develop' 2025-06-02 17:29:52 +01:00
snipe 61db37ab0d Merge remote-tracking branch 'origin/develop' 2025-06-02 15:28:08 +01:00
snipe f9c4d921e7 Merge remote-tracking branch 'origin/develop' 2025-06-02 15:07:15 +01:00
snipe ca099df573 Merge remote-tracking branch 'origin/develop' 2025-06-02 15:04:00 +01:00
snipe 28b584b8bc Merge remote-tracking branch 'origin/develop' 2025-06-02 02:08:19 +01:00
snipe 70449e694d Merge remote-tracking branch 'origin/develop' 2025-05-29 22:18:50 +01:00
snipe 8395ea552d Merge remote-tracking branch 'origin/develop' 2025-05-29 16:17:58 +01:00
snipe dc66452633 Merge remote-tracking branch 'origin/develop' 2025-05-29 13:32:41 +01:00
snipe 53ce44ac91 Merge remote-tracking branch 'origin/develop' 2025-05-29 12:38:40 +01:00
snipe c7c3243bbc Merge remote-tracking branch 'origin/develop' 2025-05-29 12:35:21 +01:00
snipe 8bdd77d33d Merge remote-tracking branch 'origin/develop' 2025-05-29 12:24:51 +01:00
snipe acd7d0db3a Merge remote-tracking branch 'origin/develop' 2025-05-29 12:09:08 +01:00
snipe 2bfadb8a3c Merge remote-tracking branch 'origin/develop' 2025-05-28 15:21:34 +01:00
snipe 0912e4af7b Merge remote-tracking branch 'origin/develop' 2025-05-26 13:17:38 +01:00
snipe 5aa5c48018 Merge remote-tracking branch 'origin/develop' 2025-05-23 19:37:42 +01:00
snipe 8cdd998f79 Merge remote-tracking branch 'origin/develop' 2025-05-23 17:35:08 +01:00
snipe 050d4d6b25 Merge remote-tracking branch 'origin/develop' 2025-05-23 14:51:48 +01:00
snipe 366cd11238 Merge remote-tracking branch 'origin/develop' 2025-05-23 14:31:10 +01:00
snipe 58d6443331 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-05-23 13:27:44 +01:00
snipe 101b8afb56 Merge remote-tracking branch 'origin/develop' 2025-05-23 13:07:47 +01:00
snipe 5df5c47945 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-05-23 10:43:58 +01:00
snipe a04740ba86 Merge remote-tracking branch 'origin/develop' 2025-05-23 10:42:44 +01:00
snipe 425ad93ac5 Merge remote-tracking branch 'origin/develop' 2025-05-23 10:17:10 +01:00
snipe 8a44144c20 Merge pull request #16954 from grokability/develop
Merge dev into master
2025-05-16 11:16:14 +02:00
snipe ee82c70582 Merge remote-tracking branch 'origin/develop' 2025-05-15 18:32:05 +02:00
snipe c87e8e606b Merge remote-tracking branch 'origin/develop' 2025-05-15 18:02:47 +02:00
snipe 37a50dd953 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-05-15 17:51:54 +02:00
snipe a2669a3084 Removed temp code
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 17:31:16 +02:00
snipe 77da22f4dd Print errors if they exist (temp)
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 17:26:04 +02:00
snipe 7830ffe202 Temp echo errors
Signed-off-by: snipe <snipe@snipe.net>
2025-05-15 17:24:08 +02:00
snipe 1c9e20d59f Merge remote-tracking branch 'origin/develop' 2025-05-15 17:10:57 +02:00
snipe 320edac286 Merge remote-tracking branch 'origin/develop' 2025-05-14 17:39:30 +02:00
snipe d49878371d Merge remote-tracking branch 'origin/develop' 2025-05-14 16:23:38 +02:00
snipe d2575a5d9b Add @JassonCordones as a contributor 2025-05-14 15:03:04 +02:00
Marcus Moore ea6cf72580 Formatting 2025-05-14 15:03:04 +02:00
Marcus Moore 2118155b37 Fix bug in getImageUrl method 2025-05-14 15:03:04 +02:00
Marcus Moore ba4f5bb71f Add test for existing functionality 2025-05-14 15:03:04 +02:00
Marcus Moore d5a74a5a8b Remove unneeded div 2025-05-14 15:03:04 +02:00
Marcus Moore 23be1df360 Remove the replaced locales form macro 2025-05-14 15:03:04 +02:00
Marcus Moore b5c1a1da4c Replace Form::locales on user setup 2025-05-14 15:03:04 +02:00
Marcus Moore c11e784f51 Replace Form::locales on bulk edit users page 2025-05-14 15:03:04 +02:00
Marcus Moore 06f51c8f9c Replace Form::locales on user edit page 2025-05-14 15:03:04 +02:00
Marcus Moore 181bcbbda6 Replace Form::locales on localization page 2025-05-14 15:03:04 +02:00
Marcus Moore d008ead6a4 Fix input name 2025-05-14 15:03:04 +02:00
Marcus Moore 75924be958 Introduce locale select component and make replacement on profile page 2025-05-14 15:03:04 +02:00
snipe b1a6e3f8a2 Merge pull request #16930 from JassonCordones/master
fix typo in snipeit.sh
2025-05-14 15:01:41 +02:00
snipe 06712a6041 Merge remote-tracking branch 'origin/develop' 2025-05-14 13:42:51 +02:00
snipe 62b16339a9 Merge remote-tracking branch 'origin/develop' 2025-05-13 20:44:34 +02:00
Jasson 9a2f1a36ba fix typo in snipeit.sh
fix redirect output to stderr
2025-05-13 14:31:37 -04:00
snipe 95cc4d3a73 Merge remote-tracking branch 'origin/develop' 2025-05-09 21:16:58 +01:00
snipe 497eeeb2e0 Merge remote-tracking branch 'origin/develop' 2025-05-09 19:29:01 +01:00
snipe 4be21ca249 Merge remote-tracking branch 'origin/develop' 2025-05-09 18:03:38 +01:00
snipe e8598e214e Merge remote-tracking branch 'origin/develop' 2025-05-09 17:23:22 +01:00
snipe 54b1d65e3c Merge remote-tracking branch 'origin/develop' 2025-05-09 14:46:37 +01:00
snipe f7648496d3 Merge remote-tracking branch 'origin/develop' 2025-05-08 16:26:02 +01:00
snipe 59a57c7197 Merge remote-tracking branch 'origin/develop' 2025-05-08 15:43:53 +01:00
snipe 5659b26827 Merge remote-tracking branch 'origin/develop' 2025-05-08 15:22:43 +01:00
snipe ee4443aaf0 Merge remote-tracking branch 'origin/develop' 2025-05-08 15:09:26 +01:00
snipe 839dcad358 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	public/css/dist/skins/_all-skins.css
#	public/css/dist/skins/_all-skins.min.css
#	public/css/dist/skins/skin-black-dark.css
#	public/css/dist/skins/skin-black-dark.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/mix-manifest.json
2025-05-07 11:41:31 +01:00
snipe d67933ab49 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	.all-contributorsrc
#	CONTRIBUTORS.md
2025-05-06 16:46:41 +01:00
Godfrey M 0eb3f6b952 set max to 5 2025-05-05 14:42:06 +01:00
Godfrey M 68b0f80fce fix input max, and help block position 2025-05-05 14:42:06 +01:00
Godfrey M 93489529a3 adds Field offset option to labels 2025-05-05 14:42:06 +01:00
snipe 511be74e74 Add @chfsx as a contributor 2025-05-05 14:42:06 +01:00
Fabian Schmid bdee067803 [FIX] set upload-limit 2025-05-05 14:42:06 +01:00
snipe 32156cace3 Merge pull request #16847 from realchrisolin/issue16214
add barcode support for Avery 3490
2025-05-05 14:40:54 +01:00
snipe 30688114be Merge remote-tracking branch 'origin/develop' 2025-05-05 14:04:52 +01:00
snipe 34088bcc17 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-05-05 11:17:01 +01:00
Calvin Schwartz 07835766cc adds support for Avery 3490 2025-05-01 21:11:42 -04:00
Brady Wetherington 251851ec6a Try to get Docker images to build for both architectures 2025-04-30 18:00:39 +01:00
snipe 049a669186 Merge remote-tracking branch 'origin/develop' 2025-04-30 16:46:16 +01:00
snipe d29f13bae9 Merge remote-tracking branch 'origin/develop' 2025-04-30 16:26:38 +01:00
snipe c758355df9 Merge remote-tracking branch 'origin/develop' 2025-04-30 16:14:34 +01:00
snipe 79d97a83af Merge remote-tracking branch 'origin/develop' 2025-04-30 15:48:38 +01:00
snipe 85bd47c240 Merge remote-tracking branch 'origin/develop' 2025-04-30 15:35:10 +01:00
snipe 473ead9616 Merge remote-tracking branch 'origin/develop' 2025-04-30 13:57:27 +01:00
snipe cf2850933c Merge remote-tracking branch 'origin/develop' 2025-04-30 10:19:43 +01:00
snipe ff2564c57a Merge remote-tracking branch 'origin/develop' 2025-04-30 10:12:05 +01:00
snipe 91d3848246 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-04-29 23:17:51 +01:00
snipe c031f0b45e Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/skins/_all-skins.css
#	public/css/dist/skins/_all-skins.min.css
#	public/css/dist/skins/skin-blue-dark.css
#	public/css/dist/skins/skin-blue-dark.min.css
#	public/css/dist/skins/skin-green-dark.css
#	public/css/dist/skins/skin-green-dark.min.css
#	public/css/dist/skins/skin-orange-dark.css
#	public/css/dist/skins/skin-orange-dark.min.css
#	public/css/dist/skins/skin-purple-dark.css
#	public/css/dist/skins/skin-purple-dark.min.css
#	public/css/dist/skins/skin-red-dark.css
#	public/css/dist/skins/skin-red-dark.min.css
#	public/css/dist/skins/skin-yellow-dark.css
#	public/css/dist/skins/skin-yellow-dark.min.css
#	public/mix-manifest.json
2025-04-29 20:38:41 +01:00
snipe fdbb9568ae Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-29 12:39:31 +01:00
snipe d817883459 Merge remote-tracking branch 'origin/develop' 2025-04-29 10:25:42 +01:00
snipe 12255979ac Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-29 10:22:21 +01:00
snipe 366b61850b Merge remote-tracking branch 'origin/develop' 2025-04-26 17:54:02 +01:00
snipe 89be6bd183 Merge remote-tracking branch 'origin/develop' 2025-04-24 14:06:00 +01:00
snipe e120331a2c Merge remote-tracking branch 'origin/develop' 2025-04-24 12:16:53 +01:00
snipe cb8a212d96 Merge remote-tracking branch 'origin/develop' 2025-04-23 21:57:20 +01:00
snipe 7aec431ac5 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:30:16 +01:00
snipe d19681dea1 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>
2025-04-23 21:05:38 +01:00
snipe bf2299daf8 Merge remote-tracking branch 'origin/develop' 2025-04-22 17:51:48 +01:00
snipe 164930d0dd Merge remote-tracking branch 'origin/develop' 2025-04-22 16:35:23 +01:00
snipe 387dbac809 Merge remote-tracking branch 'origin/develop' 2025-04-22 14:36:14 +01:00
snipe 3b661e5a99 Merge remote-tracking branch 'origin/develop' 2025-04-22 12:39:35 +01:00
snipe 90c1c0e655 Merge remote-tracking branch 'origin/develop' 2025-04-22 11:42:49 +01:00
snipe 21d8e7695b Merge remote-tracking branch 'origin/develop' 2025-04-21 20:19:54 +01:00
snipe 1acc452cfe Merge remote-tracking branch 'origin/develop' 2025-04-21 14:59:26 +01:00
snipe 1375e1feee Merge remote-tracking branch 'origin/develop' 2025-04-21 12:21:08 +01:00
snipe 2187adf59a Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>
2025-04-19 15:34:58 +01:00
snipe 0dcb315d9d Merge remote-tracking branch 'origin/develop' 2025-04-18 00:56:26 +01:00
snipe 327ccbd0c9 Merge remote-tracking branch 'origin/develop' 2025-04-18 00:37:11 +01:00
snipe f571d400e6 Merge remote-tracking branch 'origin/develop' 2025-04-17 23:29:12 +01:00
snipe 293aa52335 Merge remote-tracking branch 'origin/develop' 2025-04-17 23:13:04 +01:00
snipe ca178ae9a7 Merge remote-tracking branch 'origin/develop' 2025-04-17 22:48:35 +01:00
snipe d0c810e418 Merge remote-tracking branch 'origin/develop' 2025-04-17 16:42:37 +01:00
snipe d496d2caeb Merge remote-tracking branch 'origin/develop' 2025-04-17 11:59:24 +01:00
snipe e70b75c350 Merge remote-tracking branch 'origin/develop' 2025-04-17 01:00:34 +01:00
snipe a50befeda5 Merge remote-tracking branch 'origin/develop' 2025-04-16 20:16:18 +01:00
snipe e2616e8039 Merge remote-tracking branch 'origin/develop' 2025-04-16 15:52:18 +01:00
snipe 904266debe Merge remote-tracking branch 'origin/develop' 2025-04-16 09:47:18 +01:00
snipe 72d5783795 Merge remote-tracking branch 'origin/develop' 2025-04-16 09:19:05 +01:00
snipe d699fb1473 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 20:46:43 +01:00
snipe fab1a6c33a Merge remote-tracking branch 'origin/develop' 2025-04-15 20:30:02 +01:00
snipe be73c30194 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2025-04-15 20:01:20 +01:00
snipe 451646fe4f Prod assets
Signed-off-by: snipe <snipe@snipe.net>
2025-04-15 19:42:26 +01:00
snipe decc919991 Merge remote-tracking branch 'origin/develop' 2025-04-15 19:33:23 +01:00
snipe e0b4005921 Merge remote-tracking branch 'origin/develop' 2025-04-15 16:48:17 +01:00
snipe 3ef36e7534 Merge remote-tracking branch 'origin/develop' 2025-04-14 11:02:06 +01:00
snipe 1949e1e1e9 Merge remote-tracking branch 'origin/develop' 2025-04-14 09:26:25 +01:00
snipe 3358382358 Comment out location scoping option for now
Signed-off-by: snipe <snipe@snipe.net>
2025-04-09 21:44:38 +01:00
snipe 3eca3ecd75 Merge remote-tracking branch 'origin/develop' 2025-04-09 21:31:41 +01:00
snipe 140c6b91b0 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-09 02:40:28 +01:00
snipe a5315ec240 Merge remote-tracking branch 'origin/develop' 2025-04-08 08:32:25 +01:00
snipe 93f1656e0b Merge remote-tracking branch 'origin/develop' 2025-04-08 06:51:21 +01:00
snipe f1d006c236 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-08 05:58:51 +01:00
snipe b0b5a96694 Merge remote-tracking branch 'origin/develop' 2025-04-07 13:54:23 +01:00
snipe 7dbe9a85f4 Merge remote-tracking branch 'origin/develop' 2025-04-05 21:02:33 +01:00
snipe d2c39528d5 Merge remote-tracking branch 'origin/develop' 2025-04-05 15:44:22 +01:00
snipe 0420543c94 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-05 14:11:07 +01:00
snipe aae0db902b Merge remote-tracking branch 'origin/develop' 2025-04-03 15:41:08 +01:00
snipe 88dae7cef7 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/mix-manifest.json
2025-04-03 15:28:10 +01:00
snipe e5cb17e934 Merge remote-tracking branch 'origin/develop' 2025-04-03 10:16:18 +01:00
snipe 9d609805f2 Merge remote-tracking branch 'origin/develop' 2025-04-02 18:33:48 +01:00
snipe e2b9ca8254 Merge remote-tracking branch 'origin/develop' 2025-04-02 14:03:22 +01:00
snipe e16a2fe8af Merge remote-tracking branch 'origin/develop' 2025-04-02 13:51:09 +01:00
snipe 22d61a533d Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-04-02 12:50:35 +01:00
snipe af408bb45f Merge remote-tracking branch 'origin/develop' 2025-04-01 21:33:07 +01:00
snipe 24bfbc06f0 Merge remote-tracking branch 'origin/develop' 2025-04-01 19:40:03 +01:00
snipe 5eb9f353b5 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/all.css
#	public/css/dist/bootstrap-table.css
#	public/js/dist/bootstrap-table.js
#	public/mix-manifest.json
2025-04-01 11:25:33 +01:00
snipe 473ce15f47 Merge pull request #16526 from snipe/develop
Merge #16486  and #16519 into master
2025-03-19 15:31:50 -01:00
snipe eb9cfbaed6 Merge pull request #16498 from snipe/develop
Merge develop into master
2025-03-12 23:23:44 +00:00
snipe faeb037ff9 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/build/app.css
#	public/css/build/overrides.css
#	public/css/dist/all.css
#	public/js/build/app.js
#	public/js/dist/all.js
#	public/mix-manifest.json
2025-03-12 21:26:34 +00:00
snipe 07602f697d Merge remote-tracking branch 'origin/develop' 2025-03-12 18:13:22 +00:00
snipe 11abb0fdb1 Merge remote-tracking branch 'origin/develop' 2025-03-11 22:13:34 +00:00
snipe deeb2fa543 Merge remote-tracking branch 'origin/develop' 2025-03-11 21:14:12 +00:00
snipe ef8d5ff11e Merge remote-tracking branch 'origin/develop' 2025-03-06 12:06:10 +00:00
snipe 91f3e07b83 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-03-05 17:05:28 +00:00
snipe c29bdbdacb Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	public/css/dist/skins/_all-skins.css
#	public/css/dist/skins/_all-skins.min.css
#	public/css/dist/skins/skin-blue.css
#	public/css/dist/skins/skin-blue.min.css
#	public/mix-manifest.json
2025-03-05 13:46:29 +00:00
snipe a20d104d2f Merge remote-tracking branch 'origin/develop' 2025-03-05 11:59:47 +00:00
snipe a61dd8ac17 Merge remote-tracking branch 'origin/develop' 2025-03-05 10:52:42 +00:00
snipe 7ee9a690ea Merge remote-tracking branch 'origin/develop' 2025-03-05 01:12:22 +00:00
snipe 5ba94c6c41 Merge remote-tracking branch 'origin/develop' 2025-03-05 00:12:09 +00:00
snipe 9fa855c837 Prod assets
Signed-off-by: snipe <snipe@snipe.net>
2025-03-04 23:30:45 +00:00
snipe 9251007574 Merge remote-tracking branch 'origin/develop' 2025-03-04 23:29:31 +00:00
snipe cc73b984cb Merge remote-tracking branch 'origin/develop' 2025-03-04 21:13:43 +00:00
snipe 548ef97c32 Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-03-04 19:57:33 +00:00
snipe ed8a486726 Merge remote-tracking branch 'origin/develop' 2025-03-04 19:54:08 +00:00
snipe 1ab0911fc8 Merge remote-tracking branch 'origin/develop' 2025-03-04 19:52:16 +00:00
snipe bdbaea7294 Merge remote-tracking branch 'origin/develop' 2025-03-04 19:43:28 +00:00
snipe 5cfd1f6fb2 Merge remote-tracking branch 'origin/develop' 2025-03-04 17:16:26 +00:00
snipe 5eda67381f Merge remote-tracking branch 'origin/develop' 2025-03-04 17:07:13 +00:00
snipe 2c8b8bfaf2 Merge remote-tracking branch 'origin/develop' 2025-03-04 17:05:55 +00:00
snipe 8f3159751a Merge remote-tracking branch 'origin/develop' 2025-03-04 17:01:07 +00:00
snipe 4b05e55b29 Merge remote-tracking branch 'origin/develop' 2025-03-04 15:56:05 +00:00
snipe 3d3c13fcd0 Merge remote-tracking branch 'origin/develop' 2025-03-04 15:38:58 +00:00
snipe 88e1d8a8cf Merge remote-tracking branch 'origin/develop' 2025-03-04 15:28:53 +00:00
snipe e007db34e2 Merge remote-tracking branch 'origin/develop' 2025-03-03 22:12:26 +00:00
snipe f9f06d2c02 Merge remote-tracking branch 'origin/develop' 2025-02-27 19:08:45 +00:00
snipe 234f7d00c8 Merge remote-tracking branch 'origin/develop' 2025-02-27 16:18:18 +00:00
snipe 9924553da5 Merge remote-tracking branch 'origin/develop' 2025-02-27 15:45:57 +00:00
snipe df38d7e3ed Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net>

# Conflicts:
#	config/version.php
2025-02-27 12:22:30 +00:00
snipe 44dd061619 Merge remote-tracking branch 'origin/develop' 2025-02-26 20:55:57 +00:00
snipe 7603a932b1 Merge remote-tracking branch 'origin/develop' 2025-02-26 20:29:42 +00:00
snipe 138e7acc13 Merge remote-tracking branch 'origin/develop' 2025-02-26 12:47:54 +00:00
snipe e863d3e7e5 Merge remote-tracking branch 'origin/develop' 2025-02-26 12:02:00 +00:00
snipe c8e401f5ed Merge remote-tracking branch 'origin/develop' 2025-02-26 11:59:53 +00:00
snipe 3ba20a8e28 Merge remote-tracking branch 'origin/develop' 2025-02-26 11:39:10 +00:00
snipe ebae63752f Merge remote-tracking branch 'origin/develop' 2025-02-26 10:25:18 +00:00
snipe 8bc73901cf Merge remote-tracking branch 'origin/develop' 2025-02-26 08:25:35 +00:00
snipe b4f70d9244 Merge remote-tracking branch 'origin/develop' 2025-02-26 07:16:59 +00:00
snipe 21e9f2bba3 Merge remote-tracking branch 'origin/develop' 2025-02-26 07:11:22 +00:00
snipe 881f4e3d6a Merge remote-tracking branch 'origin/develop' 2025-02-25 14:43:57 +00:00
snipe b141945add Updated branch
Signed-off-by: snipe <snipe@snipe.net>
2025-02-25 12:18:52 +00:00
1386 changed files with 13832 additions and 153788 deletions
+1
View File
@@ -190,6 +190,7 @@ APP_ALLOW_INSECURE_HOSTS=false
GOOGLE_MAPS_API=
LDAP_MEM_LIM=500M
LDAP_TIME_LIM=600
BACKUP_TIME_LIMIT=600
IMPORT_TIME_LIMIT=600
IMPORT_MEMORY_LIMIT=500M
REPORT_TIME_LIMIT=12000
+59 -1
View File
@@ -23,7 +23,23 @@ body:
attributes:
label: Snipe-IT Version
description: What version of Snipe-IT are you seeing this issue on? You can find the version number in the footer of any page in Snipe-IT.
placeholder: ex. v8.3.1 - build 19577 (master)
placeholder: ex. v8.3.2 - build 19577 (master)
validations:
required: true
- type: input
id: php-version
attributes:
label: PHP Version
description: What version of PHP are you running? You can find the version of PHP your webserver is running in the `Admin Settings` section in the footer, and the cli version by running `php -v` via command line .
placeholder: ex. v8.3.1 (web), PHP 8.4.12 (cli)
validations:
required: true
- type: input
id: composer-version
attributes:
label: Composer Version
description: What version of composer are you running? You can find the version number by running `composer --version`.
placeholder: ex. 2.8.10
validations:
required: true
- type: input
@@ -48,6 +64,16 @@ body:
- Not sure
validations:
required: true
- type: dropdown
id: upgrade-or-fresh
attributes:
label: Is this a fresh install or an upgrade?
options:
- Fresh install
- Upgrade
- NA
validations:
required: true
- type: textarea
id: what-happened
attributes:
@@ -67,6 +93,38 @@ body:
- Safari
- Microsoft Edge
- Other
- type: dropdown
id: on-demo
attributes:
label: Can you reproduce this on the public demo?
description: You can check this at https://demo.snipeitapp.com.
options:
- 'Yes'
- 'No'
- N/A
validations:
required: true
- type: dropdown
id: fmcs
attributes:
label: Do you have full multiple company support enabled?
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
options:
- 'Yes'
- 'No'
validations:
required: true
- type: dropdown
id: fmcs-location
attributes:
label: If you have full multiple company support enabled, do you have location scoping to company enabled?
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
options:
- 'Yes'
- 'No'
- I do not have full multiple company support enabled
validations:
required: true
- type: textarea
id: server-logs
attributes:
+3 -3
View File
@@ -30,10 +30,10 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
+1 -1
View File
@@ -52,6 +52,6 @@ jobs:
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
+1 -1
View File
@@ -82,7 +82,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+1 -1
View File
@@ -81,7 +81,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Upload Laravel logs as artifacts
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
path: |
@@ -0,0 +1,59 @@
<?php
namespace App\Actions\Categories;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssetModels;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Category;
use Illuminate\Support\Facades\Storage;
class DestroyCategoryAction
{
/**
* @throws ItemStillHasAssets
* @throws ItemStillHasAssetModels
* @throws ItemStillHasComponents
* @throws ItemStillHasAccessories
* @throws ItemStillHasLicenses
* @throws ItemStillHasConsumables
*/
static function run(Category $category): bool
{
$category->loadCount([
'assets as assets_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
'licenses as licenses_count',
'models as models_count'
]);
if ($category->assets_count > 0) {
throw new ItemStillHasAssets($category);
}
if ($category->accessories_count > 0) {
throw new ItemStillHasAccessories($category);
}
if ($category->consumables_count > 0) {
throw new ItemStillHasConsumables($category);
}
if ($category->components_count > 0) {
throw new ItemStillHasComponents($category);
}
if ($category->licenses_count > 0) {
throw new ItemStillHasLicenses($category);
}
if ($category->models_count > 0) {
throw new ItemStillHasAssetModels($category);
}
Storage::disk('public')->delete('categories'.'/'.$category->image);
$category->delete();
return true;
}
}
@@ -0,0 +1,63 @@
<?php
namespace App\Actions\Manufacturers;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Manufacturer;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DeleteManufacturerAction
{
/**
* @throws ItemStillHasAssets
* @throws ItemStillHasComponents
* @throws ItemStillHasAccessories
* @throws ItemStillHasLicenses
* @throws ItemStillHasConsumables
*/
static function run(Manufacturer $manufacturer): bool
{
$manufacturer->loadCount([
'assets as assets_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
'licenses as licenses_count',
]);
if ($manufacturer->assets_count > 0) {
throw new ItemStillHasAssets($manufacturer);
}
if ($manufacturer->accessories_count > 0) {
throw new ItemStillHasAccessories($manufacturer);
}
if ($manufacturer->consumables_count > 0) {
throw new ItemStillHasConsumables($manufacturer);
}
if ($manufacturer->components_count > 0) {
throw new ItemStillHasComponents($manufacturer);
}
if ($manufacturer->licenses_count > 0) {
throw new ItemStillHasLicenses($manufacturer);
}
if ($manufacturer->image) {
try {
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
} catch (\Exception $e) {
Log::info($e);
}
}
$manufacturer->delete();
//dd($manufacturer);
return true;
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Actions\Suppliers;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Models\Supplier;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasMaintenances;
use App\Exceptions\ItemStillHasLicenses;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DestroySupplierAction
{
/**
*
* @throws ItemStillHasLicenses
* @throws ItemStillHasAssets
* @throws ItemStillHasMaintenances
* @throws ItemStillHasAccessories
* @throws ItemStillHasConsumables
* @throws ItemStillHasComponents
*/
static function run(Supplier $supplier): bool
{
$supplier->loadCount([
'maintenances as maintenances_count',
'assets as assets_count',
'licenses as licenses_count',
'accessories as accessories_count',
'consumables as consumables_count',
'components as components_count',
]);
if ($supplier->assets_count > 0) {
throw new ItemStillHasAssets($supplier);
}
if ($supplier->maintenances_count > 0) {
throw new ItemStillHasMaintenances($supplier);
}
if ($supplier->licenses_count > 0) {
throw new ItemStillHasLicenses($supplier);
}
if ($supplier->accessories_count > 0) {
throw new ItemStillHasAccessories($supplier);
}
if ($supplier->consumables_count > 0) {
throw new ItemStillHasConsumables($supplier);
}
if ($supplier->components_count > 0) {
throw new ItemStillHasComponents($supplier);
}
if ($supplier->image) {
try {
Storage::disk('public')->delete('suppliers/'.$supplier->image);
} catch (\Exception $e) {
Log::info($e->getMessage());
}
}
$supplier->delete();
return true;
}
}
+12
View File
@@ -317,9 +317,21 @@ class LdapSync extends Command
if($ldap_map["jobtitle"] != null){
$user->jobtitle = $item['jobtitle'];
}
if($ldap_map["address"] != null){
$user->address = $item['address'];
}
if($ldap_map["city"] != null){
$user->city = $item['city'];
}
if($ldap_map["state"] != null){
$user->state = $item['state'];
}
if($ldap_map["country"] != null){
$user->country = $item['country'];
}
if($ldap_map["zip"] != null){
$user->zip = $item['zip'];
}
if($ldap_map["dept"] != null){
$user->department_id = $department->id;
}
+3 -2
View File
@@ -8,8 +8,6 @@ use Symfony\Component\Console\Input\InputOption;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Helper\ProgressIndicator;
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
/**
* Class ObjectImportCommand
@@ -52,6 +50,9 @@ class ObjectImportCommand extends Command
*/
public function handle()
{
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
$this->progressIndicator = new ProgressIndicator($this->output);
$filename = $this->argument('filename');
@@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use App\Models\Actionlog;
use Illuminate\Console\Command;
class RemoveInvalidUploadDeleteActionLogItems extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:remove-invalid-upload-delete-action-log-items';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Permanently remove invalid "upload deleted" action log items that have a null filename. This command can potentially result in deleted files being "resurrected" in the UI.';
/**
* Execute the console command.
*/
public function handle()
{
$invalidLogs = Actionlog::query()
->where('action_type', 'upload deleted')
->whereNull('filename')
->withTrashed()
->get();
$this->info("{$invalidLogs->count()} invalid log items found.");
if ($invalidLogs->count() === 0) {
return 0;
}
$this->table(['ID', 'Action Type', 'Item Type', 'Item ID', 'Created At', 'Deleted At'], $invalidLogs->map(fn($log) => [
$log->id,
$log->action_type,
$log->item_type,
$log->item_id,
$log->created_at,
$log->deleted_at,
])->toArray());
if ($this->confirm("Do you wish to remove {$invalidLogs->count()} log items?")) {
$invalidLogs->each(fn($log) => $log->forceDelete());
}
return 0;
}
}
+98 -51
View File
@@ -5,6 +5,7 @@ namespace App\Console\Commands;
use Illuminate\Console\Command;
use ZipArchive;
use Illuminate\Support\Facades\Log;
use enshrined\svgSanitize\Sanitizer;
class SQLStreamer {
private $input;
@@ -242,9 +243,10 @@ class RestoreFromBackup extends Command
$private_dirs = [
'storage/private_uploads/accessories',
'storage/private_uploads/assetmodels',
'storage/private_uploads/maintenances',
'storage/private_uploads/models',
'storage/private_uploads/assetmodels' => 'storage/private_uploads/models', //this was changed from assetmodels => models Aug 10 2025
'storage/private_uploads/asset_maintenances' => 'storage/private_uploads/maintenances', //this was changed from asset_maintenances => maintenances Aug 10 2025
'storage/private_uploads/maintenances', //but let 'maintenances' take precedence
'storage/private_uploads/models', //and let 'models' take precedence
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
'storage/private_uploads/audits',
'storage/private_uploads/components',
@@ -262,7 +264,7 @@ class RestoreFromBackup extends Command
];
$public_dirs = [
'public/uploads/accessories',
'public/uploads/assetmodels',
// 'public/uploads/assetmodels' => 'public/uploads/models', //according to git, this was _never_ a thing... (see below)
'public/uploads/maintenances',
'public/uploads/assets', // these are asset _pictures_, not asset files
'public/uploads/avatars',
@@ -273,7 +275,7 @@ class RestoreFromBackup extends Command
'public/uploads/departments',
'public/uploads/locations',
'public/uploads/manufacturers',
'public/uploads/models',
'public/uploads/models', // ...it's been this way for 9 years (as of late 2025)
'public/uploads/suppliers',
];
@@ -286,8 +288,6 @@ class RestoreFromBackup extends Command
'public/uploads/favicon-uploaded.*',
];
$all_files = $private_dirs + $public_dirs;
$sqlfiles = [];
$sqlfile_indices = [];
@@ -295,6 +295,20 @@ class RestoreFromBackup extends Command
$boring_files = [];
$unsafe_files = [];
$good_extensions = config('filesystems.allowed_upload_extensions_array');
$private_extensions = array_merge($good_extensions, ["csv", "key"]); //add csv, and 'key'
$public_extensions = array_diff($good_extensions, ["xml"]); //remove xml
$sanitizer = new Sanitizer();
/**
* TODO: I _hate_ the "continue 3" thing we keep doing here
* I think a better approach might be to have the "each file" stuff be in a method on this class, and the
* boring_files and interesting_files be properties on it that we fill out. Then, in that method, we could
* just do a 'return' once the file is actually handled (yay or nay). We could also start to break out some of
* the _other_ things that we do into their own methods too? But I don't care about that as much.
*/
for ($i = 0; $i < $za->numFiles; $i++) {
$stat_results = $za->statIndex($i);
// echo "index: $i\n";
@@ -309,7 +323,7 @@ class RestoreFromBackup extends Command
// skip macOS resource fork files (?!?!?!)
if (strpos($raw_path, '__MACOSX') !== false && strpos($raw_path, '._') !== false) {
//print "SKIPPING macOS Resource fork file: $raw_path\n";
$boring_files[] = $raw_path;
// $boring_files[] = $raw_path; //stop adding this to the boring files list; it's just confusing
continue;
}
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
@@ -318,44 +332,70 @@ class RestoreFromBackup extends Command
$sqlfile_indices[] = $i;
continue;
}
if ($raw_path[-1] == '/') {
//last character is '/' - this is a directory, and we don't need it, and we don't need to warn about it
continue;
}
if (in_array(basename($raw_path), [".gitkeep", ".gitignore", ".DS_Store"])) {
//skip these boring files silently without reporting on them; they're stupid
continue;
}
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
foreach (array_merge($private_dirs, $public_dirs) as $dir) {
$last_pos = strrpos($raw_path, $dir . '/');
if ($last_pos !== false) {
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
$interesting_files[$raw_path] = ['dest' => $dir, 'index' => $i];
continue 2;
if ($last_pos + strlen($dir) + 1 == strlen($raw_path)) {
// we don't care about that; we just want files with the appropriate prefix
//print("FOUND THE EXACT DIRECTORY: $dir AT: $raw_path!!!\n");
foreach (['public' => $public_dirs, 'private' => $private_dirs] as $purpose => $dirs) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($dirs as $dir => $destdir) {
if (is_int($dir)) {
$dir = $destdir;
}
$last_pos = strrpos($raw_path, $dir . '/');
if ($last_pos !== false) {
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
//the CSV bit, below, is because we store CSV files as "blahcsv" - without an extension
if (!in_array($extension, $allowed_extensions) && !($dir == "storage/private_uploads/imports" && substr($raw_path, -3) == "csv" && $extension == "")) {
$unsafe_files[] = $raw_path;
Log::debug($raw_path . ' from directory ' . $dir . ' is being skipped');
} else {
if ($dir != $destdir) {
Log::debug("Getting ready to save file $raw_path to new directory $destdir");
}
$interesting_files[$raw_path] = ['dest' => $destdir, 'index' => $i];
}
continue 3;
}
}
}
$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) {
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
}
$last_pos = strrpos($raw_path, $file); // no trailing slash!
if ($last_pos !== false) {
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
if (!in_array($extension, $good_extensions)) {
// gathering potentially unsafe files here to return at exit
$unsafe_files[] = $raw_path;
Log::debug('Potentially unsafe file '.$raw_path.' is being skipped');
$boring_files[] = $raw_path;
continue 2;
foreach (['public' => $public_files, 'private' => $private_files] as $purpose => $files) {
$allowed_extensions = match ($purpose) {
'public' => $public_extensions,
'private' => $private_extensions,
};
foreach ($files as $file) {
$has_wildcard = (strpos($file, '*') !== false);
if ($has_wildcard) {
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
}
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//no wildcards found in $file, process 'normally'
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
continue 2;
$last_pos = strrpos($raw_path, $file); // no trailing slash!
if ($last_pos !== false) {
if (!in_array($extension, $allowed_extensions)) {
// gathering potentially unsafe files here to return at exit
$unsafe_files[] = $raw_path;
Log::debug('Potentially unsafe file ' . $raw_path . ' is being skipped');
$boring_files[] = $raw_path;
continue 3;
}
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
//no wildcards found in $file, process 'normally'
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
continue 3;
}
}
}
}
@@ -492,18 +532,25 @@ class RestoreFromBackup extends Command
}
foreach ($interesting_files as $pretty_file_name => $file_details) {
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (!is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
$migrated_file_name = $file_details['dest'] . '/' . basename($pretty_file_name);
if (strcasecmp(substr($pretty_file_name, -4), ".svg") === 0) {
$svg_contents = $za->getFromIndex($file_details['index']);
$cleaned_svg = $sanitizer->sanitize($svg_contents);
file_put_contents($migrated_file_name, $cleaned_svg);
} else {
$fp = $za->getStream($ugly_file_name);
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
if (!is_dir($file_details['dest'])) {
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
}
$migrated_file = fopen($migrated_file_name, 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
}
fclose($migrated_file);
fclose($fp);
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
}
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
fwrite($migrated_file, $buffer);
}
fclose($migrated_file);
fclose($fp);
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
if ($bar) {
$bar->advance();
}
+44 -6
View File
@@ -60,19 +60,57 @@ class SendExpirationAlerts extends Command
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
$this->table(
['ID', 'Tag', 'Model', 'Model Number', 'EOL', 'EOL Months', 'Warranty Expires', 'Warranty Months'],
$assets->map(fn($item) => ['ID' => $item->id, 'Tag' => $item->asset_tag, 'Model' => $item->model->name, 'Model Number' => $item->model->model_number, 'EOL' => $item->asset_eol_date, 'EOL Months' => $item->model->eol, 'Warranty Expires' => $item->warranty_expires, 'Warranty Months' => $item->warranty_months])
);
[
trans('general.id'),
trans('admin/hardware/form.tag'),
trans('admin/hardware/form.model'),
trans('general.model_no'),
trans('general.purchase_date'),
trans('admin/hardware/form.eol_rate'),
trans('admin/hardware/form.eol_date'),
trans('admin/hardware/form.warranty_expires'),
],
$assets->map(fn($item) =>
[
trans('general.id') => $item->id,
trans('admin/hardware/form.tag') => $item->asset_tag,
trans('admin/hardware/form.model') => $item->model->name,
trans('general.model_no') => $item->model->model_number,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/hardware/form.eol_rate') => $item->model->eol,
trans('admin/hardware/form.eol_date') => $item->eol_date ? $item->eol_formatted_date .' ('.$item->eol_diff_for_humans.')' : '',
trans('admin/hardware/form.warranty_expires') => $item->warranty_expires ? $item->warranty_expires_formatted_date .' ('.$item->warranty_expires_diff_for_humans.')' : '',
])
);
}
// Expiring licenses
$licenses = License::getExpiringLicenses($alert_interval);
$licenses = License::query()->ExpiringLicenses($alert_interval)
->with('manufacturer','category')
->orderBy('expiration_date', 'ASC')
->orderBy('termination_date', 'ASC')
->get();
if ($licenses->count() > 0) {
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $alert_interval));
$this->table(
['ID', 'Name', 'Expires', 'Termination Date'],
$licenses->map(fn($item) => ['ID' => $item->id, 'Name' => $item->name, 'Expires' => $item->expiration_date, 'Termination Date' => $item->termination_date])
[
trans('general.id'),
trans('general.name'),
trans('general.purchase_date'),
trans('admin/licenses/form.expiration'),
trans('mail.expires'),
trans('admin/licenses/form.termination_date'),
trans('mail.terminates')],
$licenses->map(fn($item) => [
trans('general.id') => $item->id,
trans('general.name') => $item->name,
trans('general.purchase_date') => $item->purchase_date_formatted,
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
trans('mail.expires') => $item->expires_formatted_date ? $item->expires_diff_for_humans : '',
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
trans('mail.terminates') => $item->terminates_diff_for_humans
])
);
}
@@ -16,7 +16,7 @@ class SendUpcomingAuditReport extends Command
*
* @var string
*/
protected $signature = 'snipeit:upcoming-audits';
protected $signature = 'snipeit:upcoming-audits {--with-output : Display the results in a table in your console in addition to sending the email}';
/**
* The console command description.
@@ -47,21 +47,69 @@ class SendUpcomingAuditReport extends Command
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);
$assets = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
$this->info($assets->count() . ' assets must be audited in on or before ' . $interval_date . ' is deadline');
if ((count($assets) !== 0) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item))
->filter(fn($item) => !empty($item))
->all();
$this->info('Sending Admin SendUpcomingAuditNotification to: ' . $settings->alert_email);
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets, $settings->audit_warning_days));
$assets_query = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'asc')->with('supplier');
$asset_count = $assets_query->count();
$this->info(number_format($asset_count) . ' assets must be audited on or before ' . $interval_date);
if (!$this->option('with-output')) {
$this->info('Run this command with the --with-output option to see the full list in the console.');
}
if ($asset_count > 0) {
$assets_for_email = $assets_query->limit(30)->get();
// Send a rollup to the admin, if settings dictate
if ($settings->alert_email != '') {
$recipients = collect(explode(',', $settings->alert_email))
->map(fn($item) => trim($item))
->filter(fn($item) => !empty($item))
->all();
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets_for_email, $settings->audit_warning_days, $asset_count));
$this->info('Audit notification sent to: ' . $settings->alert_email);
} else {
$this->info('There is no admin alert email set so no email will be sent.');
}
if ($this->option('with-output')) {
// Get the full list if the user wants output in the console
$assets_for_output = $assets_query->limit(null)->get();
$this->table(
[
trans('general.id'),
trans('general.name'),
trans('general.last_audit'),
trans('general.next_audit_date'),
trans('mail.Days'),
trans('mail.supplier'),
trans('mail.assigned_to'),
],
$assets_for_output->map(fn($item) => [
trans('general.id') => $item->id,
trans('general.name') => $item->display_name,
trans('general.last_audit') => $item->last_audit_formatted_date,
trans('general.next_audit_date') => $item->next_audit_formatted_date,
trans('mail.Days') => round($item->next_audit_diff_in_days),
trans('mail.supplier') => $item->supplier ? $item->supplier->name : '',
trans('mail.assigned_to') => $item->assignedTo ? $item->assignedTo->display_name : '',
])
);
}
} else {
$this->info('There are no assets due for audit in the next ' . $interval . ' days.');
}
}
}
+2
View File
@@ -37,6 +37,8 @@ class SystemBackup extends Command
*/
public function handle()
{
ini_set('max_execution_time', env('BACKUP_TIME_LIMIT', 600)); //600 seconds = 10 minutes
if ($this->option('filename')) {
$filename = $this->option('filename');
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasAccessories extends ItemStillHasChildren
{
//
}
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasAssetModels extends ItemStillHasChildren
{
//
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasAssets extends ItemStillHasChildren
{
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasChildren extends Exception
{
//public function __construct($message, $code = 0, Exception $previous = null, $parent, $children)
//{
// trans()
//
//}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasComponents extends ItemStillHasChildren
{
//
}
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasConsumables extends ItemStillHasChildren
{
//
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasLicenses extends ItemStillHasChildren
{
//
}
@@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class ItemStillHasMaintenances extends ItemStillHasChildren
{
//
}
+7 -1
View File
@@ -40,6 +40,8 @@ class IconHelper
return 'fa-solid fa-trash-arrow-up';
case 'external-link':
return 'fa fa-external-link';
case 'link':
return 'fa fa-link';
case 'email':
return 'fa-regular fa-envelope';
case 'phone':
@@ -140,7 +142,7 @@ class IconHelper
case 'more-files':
return 'fa-solid fa-laptop-file';
case 'maintenances':
return 'fas fa-wrench';
return 'fa-solid fa-screwdriver-wrench';
case 'seats':
return 'far fa-list-alt';
case 'globe-us':
@@ -195,6 +197,10 @@ class IconHelper
case 'note':
case 'notes':
return 'fas fa-sticky-note';
case 'tip':
return 'fa-solid fa-lightbulb';
case 'highlight':
return 'fa-solid fa-highlighter';
}
}
}
@@ -77,7 +77,6 @@ class AcceptanceController extends Controller
$acceptance = CheckoutAcceptance::find($id);
$assigned_user = User::find($acceptance->assigned_to_id);
$settings = Setting::getSettings();
$path_logo = '';
$sig_filename='';
@@ -117,8 +116,6 @@ class AcceptanceController extends Controller
$item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id);
// If signatures are required, make sure we have one
if (Setting::getSettings()->require_accept_signature == '1') {
@@ -138,6 +135,13 @@ class AcceptanceController extends Controller
}
// Convert PDF logo to base64 for TCPDF
// This is needed for TCPDF to properly embed the image if it's a png and the cache isn't writable
$encoded_logo = null;
if ($settings->acceptance_pdf_logo) {
$encoded_logo = base64_encode(file_get_contents(public_path() . '/uploads/' . $settings->acceptance_pdf_logo));
}
// Get the data array ready for the notifications and PDF generation
$data = [
'item_tag' => $item->asset_tag,
@@ -151,16 +155,16 @@ class AcceptanceController extends Controller
'accepted_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
'declined_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
'assigned_to' => $assigned_user->display_name,
'email' => $assigned_user->email,
'employee_num' => $assigned_user->employee_num,
'site_name' => $settings->site_name,
'company_name' => $item->company?->name?? $settings->site_name,
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
'logo' => ($settings->acceptance_pdf_logo) ? public_path() . '/uploads/' . $settings->acceptance_pdf_logo : null,
'signature' => (($sig_filename && array_key_exists('1', $encoded_image))) ? $encoded_image[1] : null,
'logo' => ($encoded_logo) ?? null,
'date_settings' => $settings->date_display_format,
'admin' => auth()->user()->present()?->fullName,
'qty' => $acceptance->qty ?? 1,
];
if ($request->input('asset_acceptance') == 'accepted') {
@@ -54,6 +54,15 @@ class AccessoriesController extends Controller
'notes',
'checkouts_count',
'qty',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'location',
'category',
'supplier',
'manufacturer',
];
@@ -61,10 +70,23 @@ class AccessoriesController extends Controller
->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier', 'adminuser')
->withCount('checkouts as checkouts_count');
if ($request->filled('search')) {
$accessories = $accessories->TextSearch($request->input('search'));
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$accessories->ByFilter($filter);
} elseif ($request->filled('search')) {
$accessories->TextSearch($request->input('search'));
}
if ($request->filled('company_id')) {
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
}
@@ -46,11 +46,20 @@ class AssetModelsController extends Controller
'manufacturer',
'requestable',
'assets_count',
'assets_assigned_count',
'assets_archived_count',
'remaining',
'category',
'fieldset',
'deleted_at',
'updated_at',
'require_serial',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'manufacturer',
'category',
];
$assetmodels = AssetModel::select([
@@ -73,7 +82,28 @@ class AssetModelsController extends Controller
'models.require_serial'
])
->with('category', 'depreciation', 'manufacturer', 'fieldset.fields.defaultValues', 'adminuser')
->withCount('assets as assets_count');
->withCount('assets as assets_count')
->withCount('availableAssets as remaining')
->withCount('assignedAssets as assets_assigned_count')
->withCount('archivedAssets as assets_archived_count');
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$assetmodels->ByFilter($filter);
} elseif ($request->filled('search')) {
$assetmodels->TextSearch($request->input('search'));
}
if ($request->input('status')=='deleted') {
$assetmodels->onlyTrashed();
+31 -1
View File
@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Http\Transformers\ComponentsTransformer;
use App\Models\AccessoryCheckout;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
@@ -115,6 +116,22 @@ class AssetsController extends Controller
'asset_eol_date',
'requestable',
'jobtitle',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'model',
'location',
'rtd_location',
'category',
'status_label',
'manufacturer',
'supplier',
'jobtitle',
'assigned_to',
'created_by',
];
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
@@ -131,6 +148,7 @@ class AssetsController extends Controller
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
$assets = Asset::select('assets.*')
@@ -165,7 +183,7 @@ class AssetsController extends Controller
// Search custom fields by column name
foreach ($all_custom_fields as $field) {
if ($request->filled($field->db_column_name()) && $field->db_column_name()) {
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
$assets->where('assets.'.$field->db_column_name(), '=', $request->input($field->db_column_name()));
}
}
@@ -1322,6 +1340,18 @@ class AssetsController extends Controller
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
public function assignedComponents(Request $request, Asset $asset): JsonResponse|array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$asset->loadCount('components');
$total = $asset->components_count;
$components = $asset->load(['components' => fn($query) => $query->applyOffsetAndLimit($total)])->components;
return (new ComponentsTransformer)->transformComponents($components, $total);
}
/**
* Generate asset labels by tag
@@ -2,6 +2,8 @@
namespace App\Http\Controllers\Api;
use App\Actions\Categories\DestroyCategoryAction;
use App\Exceptions\ItemStillHasChildren;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\CategoriesTransformer;
@@ -61,6 +63,23 @@ class CategoriesController extends Controller
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$categories->ByFilter($filter);
} elseif ($request->filled('search')) {
$categories->TextSearch($request->input('search'));
}
/*
* This checks to see if we should override the Admin Setting to show archived assets in list.
* We don't currently use it within the Snipe-IT GUI, but will be useful for API integrations where they
@@ -74,10 +93,6 @@ class CategoriesController extends Controller
$categories = $categories->withCount('showableAssets as assets_count');
}
if ($request->filled('search')) {
$categories = $categories->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$categories->where('name', '=', $request->input('name'));
}
@@ -211,17 +226,21 @@ class CategoriesController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id) : JsonResponse
public function destroy(Category $category): JsonResponse
{
$this->authorize('delete', Category::class);
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
if (! $category->isDeletable()) {
try {
DestroyCategoryAction::run(category: $category);
} catch (ItemStillHasChildren $e) {
return response()->json(
Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>$category->category_type]))
Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['asset_type' => $category->category_type]))
);
} catch (\Exception $e) {
report($e);
return response()->json(
Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong'))
);
}
$category->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
}
@@ -45,16 +45,40 @@ class ComponentsController extends Controller
'qty',
'image',
'notes',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'location',
'category',
'manufacturer',
'supplier',
];
$components = Component::select('components.*')
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer', 'uncontrainedAssets')
->withSum('uncontrainedAssets', 'components_assets.assigned_qty');
if ($request->filled('search')) {
$components = $components->TextSearch($request->input('search'));
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$components->ByFilter($filter);
} elseif ($request->filled('search')) {
$components->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$components->where('name', '=', $request->input('name'));
}
@@ -31,10 +31,53 @@ class ConsumablesController extends Controller
$consumables = Consumable::with('company', 'location', 'category', 'supplier', 'manufacturer')
->withCount('users as consumables_users_count');
if ($request->filled('search')) {
$consumables = $consumables->TextSearch(e($request->input('search')));
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
// These must match a column on the consumables table directly.
$allowed_columns = [
'id',
'name',
'order_number',
'min_amt',
'purchase_date',
'purchase_cost',
'company',
'category',
'model_number',
'item_no',
'manufacturer',
'location',
'qty',
'image',
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'location',
'category',
'supplier',
'manufacturer',
];
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$consumables->ByFilter($filter);
} elseif ($request->filled('search')) {
$consumables->TextSearch($request->input('search'));
}
if ($request->filled('name')) {
$consumables->where('name', '=', $request->input('name'));
}
@@ -96,25 +139,6 @@ class ConsumablesController extends Controller
$consumables = $consumables->OrderByCreatedBy($order);
break;
default:
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
// These must match a column on the consumables table directly.
$allowed_columns = [
'id',
'name',
'order_number',
'min_amt',
'purchase_date',
'purchase_cost',
'company',
'category',
'model_number',
'item_no',
'manufacturer',
'location',
'qty',
'image'
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
$consumables = $consumables->orderBy($sort, $order);
break;
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\License;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\JsonResponse;
@@ -25,9 +26,12 @@ class LicensesController extends Controller
$this->authorize('view', License::class);
$licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count');
$settings = Setting::getSettings();
if ($request->input('status')=='inactive') {
$licenses->ExpiredLicenses();
} elseif ($request->input('status')=='expiring') {
$licenses->ExpiringLicenses($settings->alert_interval);
} else {
$licenses->ActiveLicenses();
}
@@ -52,6 +52,10 @@ class MaintenancesController extends Controller
$maintenances->where('maintenances.created_by', '=', $request->input('created_by'));
}
if ($request->filled('url')) {
$maintenances->where('maintenances.url', '=', $request->input('url'));
}
if ($request->filled('asset_maintenance_type')) {
$maintenances->where('asset_maintenance_type', '=', $request->input('asset_maintenance_type'));
}
@@ -2,6 +2,13 @@
namespace App\Http\Controllers\Api;
use App\Actions\Manufacturers\DeleteManufacturerAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasChildren;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\ManufacturersTransformer;
@@ -184,19 +191,19 @@ class ManufacturersController extends Controller
* @since [v4.0]
* @param int $id
*/
public function destroy($id) : JsonResponse
public function destroy(Manufacturer $manufacturer): JsonResponse
{
$this->authorize('delete', Manufacturer::class);
$manufacturer = Manufacturer::findOrFail($id);
$this->authorize('delete', $manufacturer);
if ($manufacturer->isDeletable()) {
$manufacturer->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
try {
DeleteManufacturerAction::run($manufacturer);
} catch (ItemStillHasChildren $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')])));
} catch (\Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.assoc_users')));
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
}
/**
@@ -2,6 +2,13 @@
namespace App\Http\Controllers\Api;
use App\Actions\Suppliers\DestroySupplierAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasMaintenances;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasLicenses;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Transformers\SelectlistTransformer;
@@ -191,27 +198,40 @@ class SuppliersController extends Controller
* @since [v4.0]
* @param int $id
*/
public function destroy($id) : JsonResponse
public function destroy(Supplier $supplier): JsonResponse
{
$this->authorize('delete', Supplier::class);
$supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
$this->authorize('delete', $supplier);
if ($supplier->assets_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
try {
DestroySupplierAction::run(supplier: $supplier);
} catch (ItemStillHasAssets $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_assets', [
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
])));
} catch (ItemStillHasMaintenances $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_maintenances', [
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
])));
} catch (ItemStillHasLicenses $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_licenses', [
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
])));
} catch (ItemStillHasAccessories $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_accessories', [
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
])));
} catch (ItemStillHasConsumables $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_consumables', [
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
])));
} catch (ItemStillHasComponents $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_components', [
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
])));
} catch (\Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
if ($supplier->maintenances_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count])));
}
if ($supplier->licenses_count > 0) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
}
$supplier->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
}
@@ -193,8 +193,12 @@ class UploadedFilesController extends Controller
// Check for the file
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
->where('item_id', $object->id)->first();
$log = Actionlog::query()
->where('id', $file_id)
->where('action_type', 'uploaded')
->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
@@ -213,4 +217,4 @@ class UploadedFilesController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
}
}
}
+68 -45
View File
@@ -103,9 +103,75 @@ class UsersController extends Controller
'managedLocations as manages_locations_count'
]);
$allowed_columns =
[
'last_name',
'first_name',
'display_name',
'email',
'jobtitle',
'username',
'employee_num',
'groups',
'activated',
'created_at',
'updated_at',
'two_factor_enrolled',
'two_factor_optin',
'last_login',
'assets_count',
'licenses_count',
'consumables_count',
'accessories_count',
'manages_users_count',
'manages_locations_count',
'phone',
'mobile',
'address',
'city',
'state',
'country',
'zip',
'id',
'ldap_import',
'two_factor_optin',
'two_factor_enrolled',
'remote',
'vip',
'start_date',
'end_date',
'autoassign_licenses',
'website',
'locale',
'notes',
'employee_num',
if ($request->filled('search') != '') {
$users = $users->TextSearch($request->input('search'));
// These are *relationships* so we wouldn't normally include them in this array,
// since they would normally create a `column not found` error,
// BUT we account for them in the ordering switch down at the end of this method
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
'company',
'location',
'department',
'manager',
'created_by',
];
$filter = [];
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
return in_array($key, $allowed_columns);
}, ARRAY_FILTER_USE_KEY);
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$users->ByFilter($filter);
} elseif ($request->filled('search')) {
$users->TextSearch($request->input('search'));
}
if ($request->filled('activated')) {
@@ -286,49 +352,6 @@ class UsersController extends Controller
$users->orderBy('first_name', $order);
break;
default:
$allowed_columns =
[
'last_name',
'first_name',
'display_name',
'email',
'jobtitle',
'username',
'employee_num',
'groups',
'activated',
'created_at',
'updated_at',
'two_factor_enrolled',
'two_factor_optin',
'last_login',
'assets_count',
'licenses_count',
'consumables_count',
'accessories_count',
'manages_users_count',
'manages_locations_count',
'phone',
'mobile',
'address',
'city',
'state',
'country',
'zip',
'id',
'ldap_import',
'two_factor_optin',
'two_factor_enrolled',
'remote',
'vip',
'start_date',
'end_date',
'autoassign_licenses',
'website',
'locale',
'notes',
];
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
$users = $users->orderBy($sort, $order);
break;
@@ -363,7 +363,7 @@ class AssetsController extends Controller
$asset->purchase_cost = $request->input('purchase_cost', null);
$asset->purchase_date = $request->input('purchase_date', null);
$asset->next_audit_date = $request->input('next_audit_date', null);
if ($request->filled('purchase_date') && !$request->filled('asset_eol_date') && ($asset->model->eol > 0)) {
if ($request->filled('purchase_date') && !$request->filled('asset_eol_date') && ($asset->model?->eol > 0)) {
$asset->purchase_date = $request->input('purchase_date', null);
$asset->asset_eol_date = Carbon::parse($request->input('purchase_date'))->addMonths($asset->model->eol)->format('Y-m-d');
$asset->eol_explicit = false;
@@ -379,7 +379,7 @@ class AssetsController extends Controller
} else {
$asset->eol_explicit = true;
}
} elseif (!$request->filled('asset_eol_date') && (($asset->model->eol) == 0)) {
} elseif (!$request->filled('asset_eol_date') && (($asset->model?->eol) == 0)) {
$asset->asset_eol_date = null;
$asset->eol_explicit = false;
}
@@ -398,6 +398,7 @@ class AssetsController extends Controller
$asset->assigned_to = null;
$asset->assigned_type = null;
$asset->accepted = null;
$asset->last_checkin = now();
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update with '.$status->getStatuslabelType().' status', date('Y-m-d H:i:s'), $originalValues));
}
@@ -163,7 +163,7 @@ class BulkAssetsController extends Controller
$modelNames = [];
foreach($models as $model) {
$modelNames[] = $model->model->name;
$modelNames[] = $model->model?->name;
}
if ($request->filled('bulk_actions')) {
@@ -240,10 +240,6 @@ class BulkAssetsController extends Controller
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
}
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
@@ -274,6 +270,7 @@ class BulkAssetsController extends Controller
|| ($request->filled('company_id'))
|| ($request->filled('status_id'))
|| ($request->filled('model_id'))
|| ($request->filled('notes'))
|| ($request->filled('next_audit_date'))
|| ($request->filled('asset_eol_date'))
|| ($request->filled('null_name'))
@@ -470,7 +467,7 @@ class BulkAssetsController extends Controller
*/
// Does the model have a fieldset?
if ($asset->model->fieldset) {
if ($asset->model?->fieldset) {
foreach ($asset->model->fieldset->fields as $field) {
// null custom fields
@@ -621,9 +618,25 @@ class BulkAssetsController extends Controller
{
$this->authorize('checkout', Asset::class);
$alreadyAssigned = collect();
if (old('selected_assets') && is_array(old('selected_assets'))) {
$assets = Asset::findMany(old('selected_assets'));
[$assignable, $alreadyAssigned] = $assets->partition(function (Asset $asset) {
return !$asset->assigned_to;
});
session()->flashInput(['selected_assets' => $assignable->pluck('id')->values()->toArray()]);
}
$do_not_change = ['' => trans('general.do_not_change')];
$status_label_list = $do_not_change + Helper::deployableStatusLabelList();
return view('hardware/bulk-checkout')->with('statusLabel_list', $status_label_list);
return view('hardware/bulk-checkout', [
'statusLabel_list' => $status_label_list,
'removed_assets' => $alreadyAssigned,
]);
}
/**
@@ -656,6 +669,21 @@ class BulkAssetsController extends Controller
->with('error', trans('general.error_assets_already_checked_out'));
}
// Prevent checking out assets across companies if FMCS enabled
if (Setting::getSettings()->full_multiple_companies_support && $target->company_id) {
$company_ids = $assets->pluck('company_id')->unique();
// if there is more than one unique company id or the singular company id does not match
// then the checkout is invalid
if ($company_ids->count() > 1 || $company_ids->first() != $target->company_id) {
// re-add the asset ids so the assets select is re-populated
$request->session()->flashInput(['selected_assets' => $asset_ids]);
return redirect(route('hardware.bulkcheckout.show'))
->with('error', trans('general.error_user_company_multiple'));
}
}
if (request('checkout_to_type') == 'asset') {
foreach ($asset_ids as $asset_id) {
if ($target->id == $asset_id) {
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Categories\DestroyCategoryAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssetModels;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Category;
use Illuminate\Http\Request;
class BulkCategoriesController extends Controller
{
public function destroy(Request $request)
{
$this->authorize('delete', Category::class);
$errors = [];
$success_count = 0;
foreach ($request->ids as $id) {
$category = Category::find($id);
if (is_null($category)) {
$errors[] = trans('admin/categories/message.does_not_exist');
continue;
}
try {
DestroyCategoryAction::run(category: $category);
$success_count++;
} catch (ItemStillHasAccessories $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
} catch (ItemStillHasAssetModels) {
$errors[] = trans('general.bulk_delete_associations.assoc_asset_models_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
} catch (ItemStillHasAssets) {
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
} catch (ItemStillHasComponents) {
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
} catch (ItemStillHasConsumables) {
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
} catch (ItemStillHasLicenses) {
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);;
} catch (\Exception $e) {
report($e);
$errors[] = trans('general.something_went_wrong');
}
}
if (count($errors) > 0) {
if ($success_count > 0) {
return redirect()->route('categories.index')->with('success', trans_choice('admin/categories/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
}
return redirect()->route('categories.index')->with('multi_error_messages', $errors);
} else {
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.bulk_success'));
}
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Manufacturers\DeleteManufacturerAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssetModels;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasChildren;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Manufacturer;
use Illuminate\Http\Request;
class BulkManufacturersController extends Controller
{
public function destroy(Request $request)
{
$this->authorize('delete', Manufacturer::class);
$errors = [];
$success_count = 0;
foreach ($request->ids as $id) {
$manufacturer = Manufacturer::find($id);
if (is_null($manufacturer)) {
$errors[] = trans('admin/manufacturers/message.does_not_exist');
continue;
}
try {
DeleteManufacturerAction::run(manufacturer: $manufacturer);
$success_count++;
} catch (ItemStillHasAssets $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
} catch (ItemStillHasAccessories $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_accessories_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
} catch (ItemStillHasConsumables $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
} catch (ItemStillHasComponents $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
} catch (ItemStillHasLicenses $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);;
} catch (\Exception $e) {
report($e);
$errors[] = trans('general.something_went_wrong');
}
}
if (count($errors) > 0) {
if ($success_count > 0) {
return redirect()->route('manufacturers.index')->with('success', trans_choice('admin/manufacturers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
}
return redirect()->route('manufacturers.index')->with('multi_error_messages', $errors);
} else {
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.bulk_success'));
}
}
}
@@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Actions\Suppliers\DestroySupplierAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasMaintenances;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasLicenses;
use App\Models\Supplier;
use Illuminate\Http\Request;
class BulkSuppliersController extends Controller
{
public function destroy(Request $request)
{
$this->authorize('delete', Supplier::class);
$errors = [];
$success_count = 0;
foreach ($request->ids as $id) {
$supplier = Supplier::find($id);
if (is_null($supplier)) {
$errors[] = trans('admin/suppliers/message.delete.not_found');
continue;
}
try {
DestroySupplierAction::run(supplier: $supplier);
} catch (ItemStillHasAssets $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_assets', ['asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (ItemStillHasMaintenances $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (ItemStillHasLicenses $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (ItemStillHasAccessories $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_accessories', ['accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (ItemStillHasConsumables $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_consumables', ['consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (ItemStillHasComponents $e) {
$errors[] = trans('general.bulk_delete_associations.assoc_components', ['components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
} catch (\Exception $e) {
report($e);
$errors[] = trans('general.something_went_wrong');
}
}
if (count($errors) > 0) {
if ($success_count > 0) {
return redirect()->route('suppliers.index')->with('success', trans_choice('admin/suppliers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
}
return redirect()->route('suppliers.index')->with('multi_error_messages', $errors);
} else {
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.bulk_success'));
}
}
}
+16 -10
View File
@@ -2,6 +2,14 @@
namespace App\Http\Controllers;
use App\Actions\Categories\DestroyCategoryAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssetModels;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasChildren;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Category;
@@ -143,20 +151,18 @@ class CategoriesController extends Controller
* @since [v1.0]
* @param int $categoryId
*/
public function destroy($categoryId) : RedirectResponse
public function destroy(Category $category): RedirectResponse
{
$this->authorize('delete', Category::class);
// Check if the category exists
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
try {
DestroyCategoryAction::run($category);
} catch (ItemStillHasChildren $e) {
return redirect()->route('categories.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.category')]));
} catch (\Exception $e) {
report($e);
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.delete.error'));
}
if (! $category->isDeletable()) {
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=> $category->category_type]));
}
Storage::disk('public')->delete('categories'.'/'.$category->image);
$category->delete();
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
}
+3
View File
@@ -45,6 +45,7 @@ abstract class Controller extends BaseController
'accessories' => Accessory::class,
'maintenances' => Maintenance::class,
'assets' => Asset::class,
'audits' => Asset::class,
'components' => Component::class,
'consumables' => Consumable::class,
'hardware' => Asset::class,
@@ -58,6 +59,7 @@ abstract class Controller extends BaseController
'accessories' => 'private_uploads/accessories/',
'maintenances' => 'private_uploads/maintenances/',
'assets' => 'private_uploads/assets/',
'audits' => 'private_uploads/audits/',
'components' => 'private_uploads/components/',
'consumables' => 'private_uploads/consumables/',
'hardware' => 'private_uploads/assets/',
@@ -71,6 +73,7 @@ abstract class Controller extends BaseController
'accessories' => 'accessory',
'maintenances' => 'maintenance',
'assets' => 'asset',
'audits' => 'audits',
'components' => 'component',
'consumables' => 'consumable',
'hardware' => 'asset',
+7 -1
View File
@@ -65,6 +65,7 @@ class GroupsController extends Controller
$group->notes = $request->input('notes');
if ($group->save()) {
$group->users()->sync($request->input('associated_users'));
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
}
@@ -88,7 +89,10 @@ class GroupsController extends Controller
$groupPermissions = [];
}
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
$associated_users = $group->users()->get();
//dd($associated_users->toArray());
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))->with('associated_users', $associated_users);
}
/**
@@ -105,8 +109,10 @@ class GroupsController extends Controller
$group->permissions = json_encode($request->input('permission'));
$group->notes = $request->input('notes');
if (! config('app.lock_passwords')) {
if ($group->save()) {
$group->users()->sync($request->input('associated_users'));
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.update'));
}
@@ -68,6 +68,12 @@ class MaintenancesController extends Controller
{
$this->authorize('update', Asset::class);
\Log::error(print_r($request->input('selected_assets[]'), true));
if (!$request->filled('selected_assets')) {
return redirect()->back()->withInput()->with('error', 'No assets were selected.');
}
$assets = Asset::whereIn('id', $request->input('selected_assets'))->get();
// Loop through the selected assets
@@ -78,6 +84,7 @@ class MaintenancesController extends Controller
$maintenance->is_warranty = $request->input('is_warranty');
$maintenance->cost = $request->input('cost');
$maintenance->notes = $request->input('notes');
$maintenance->url = $request->input('url');
// Save the asset maintenance data
$maintenance->asset_id = $asset->id;
@@ -152,6 +159,7 @@ class MaintenancesController extends Controller
$maintenance->name = $request->input('name');
$maintenance->start_date = $request->input('start_date');
$maintenance->completion_date = $request->input('completion_date');
$maintenance->url = $request->input('url');
// Todo - put this in a getter/setter?
@@ -2,6 +2,14 @@
namespace App\Http\Controllers;
use App\Actions\Manufacturers\DeleteManufacturerAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasChildren;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasLicenses;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Actionlog;
use App\Models\Manufacturer;
@@ -157,32 +165,18 @@ class ManufacturersController extends Controller
* @param int $manufacturerId
* @since [v1.0]
*/
public function destroy($manufacturerId) : RedirectResponse
public function destroy(Manufacturer $manufacturer): RedirectResponse
{
$this->authorize('delete', Manufacturer::class);
if (is_null($manufacturer = Manufacturer::withTrashed()->withCount('models as models_count')->find($manufacturerId))) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
$this->authorize('delete', $manufacturer);
try {
DeleteManufacturerAction::run($manufacturer);
} catch (ItemStillHasChildren $e) {
return redirect()->route('manufacturers.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')]));
} catch (\Exception $e) {
report($e);
return redirect()->route('manufacturers.index')->with('error', trans('general.something_went_wrong'));
}
if (! $manufacturer->isDeletable()) {
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.assoc_users'));
}
if ($manufacturer->image) {
try {
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
} catch (\Exception $e) {
Log::info($e);
}
}
// Soft delete the manufacturer if active, permanent delete if is already deleted
if ($manufacturer->deleted_at === null) {
$manufacturer->delete();
} else {
$manufacturer->forceDelete();
}
// Redirect to the manufacturers management page
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
}
+10 -4
View File
@@ -436,10 +436,8 @@ class ReportsController extends Controller
// Open output stream
$handle = fopen('php://output', 'w');
stream_set_timeout($handle, 2000);
if ($request->filled('use_bom')) {
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
}
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
$header = [];
@@ -685,6 +683,14 @@ class ReportsController extends Controller
$assets->whereBetween('assets.purchase_date', [$request->input('purchase_start'), $request->input('purchase_end')]);
}
if ($request->filled('purchase_cost_start')) {
if ($request->filled('purchase_cost_end')) {
$assets->whereBetween('assets.purchase_cost', [$request->input('purchase_cost_start'), $request->input('purchase_cost_end')]);
} else {
$assets->where('assets.purchase_cost', ">", $request->input('purchase_cost_start'));
}
}
if (($request->filled('created_start')) && ($request->filled('created_end'))) {
$created_start = Carbon::parse($request->input('created_start'))->startOfDay();
$created_end = Carbon::parse($request->input('created_end'))->endOfDay();
+39 -21
View File
@@ -2,10 +2,18 @@
namespace App\Http\Controllers;
use App\Actions\Suppliers\DestroySupplierAction;
use App\Exceptions\ItemStillHasAccessories;
use App\Exceptions\ItemStillHasComponents;
use App\Exceptions\ItemStillHasConsumables;
use App\Exceptions\ItemStillHasMaintenances;
use App\Exceptions\ItemStillHasAssets;
use App\Exceptions\ItemStillHasLicenses;
use App\Http\Requests\ImageUploadRequest;
use App\Models\Supplier;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Illuminate\Support\MessageBag;
/**
* This controller handles all actions related to Suppliers for
@@ -118,30 +126,41 @@ class SuppliersController extends Controller
*
* @param int $supplierId
*/
public function destroy($supplierId) : RedirectResponse
public function destroy(Supplier $supplier): RedirectResponse
{
$this->authorize('delete', Supplier::class);
if (is_null($supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->find($supplierId))) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
try {
DestroySupplierAction::run(supplier: $supplier);
} catch (ItemStillHasAssets $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_assets', [
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
]));
} catch (ItemStillHasMaintenances $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_maintenances', [
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
]));
} catch (ItemStillHasLicenses $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_licenses', [
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
]));
} catch (ItemStillHasAccessories $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_accessories', [
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
]));
} catch (ItemStillHasConsumables $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_consumables', [
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
]));
} catch (ItemStillHasComponents $e) {
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_components', [
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
]));
} catch (\Exception $e) {
report($e);
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.error'));
}
if ($supplier->assets_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
}
if ($supplier->maintenances_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count]));
}
if ($supplier->licenses_count > 0) {
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count]));
}
$supplier->delete();
return redirect()->route('suppliers.index')->with('success',
trans('admin/suppliers/message.delete.success')
);
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.success'));
}
/**
@@ -154,6 +173,5 @@ class SuppliersController extends Controller
{
$this->authorize('view', Supplier::class);
return view('suppliers/view', compact('supplier'));
}
}
@@ -14,6 +14,15 @@ class CustomAssetReportRequest extends Request
return true;
}
public function prepareForValidation()
{
if($this->filled('purchase_cost_end') && !$this->filled('purchase_cost_start')){
$this->merge(['purchase_cost_start' => 0 ]);
}
}
/**
* Get the validation rules that apply to the request.
*
@@ -24,6 +33,7 @@ class CustomAssetReportRequest extends Request
return [
'purchase_start' => 'date|date_format:Y-m-d|nullable',
'purchase_end' => 'date|date_format:Y-m-d|nullable',
'purchase_cost_end' => 'numeric|nullable|gte:purchase_cost_start',
'created_start' => 'date|date_format:Y-m-d|nullable',
'created_end' => 'date|date_format:Y-m-d|nullable',
'checkout_date_start' => 'date|date_format:Y-m-d|nullable',
+1 -1
View File
@@ -109,7 +109,7 @@ class SettingsSamlRequest extends FormRequest
];
$pkey = openssl_pkey_new([
'private_key_bits' => config('app.saml_key_size'),
'private_key_bits' => (int) config('app.saml_key_size'),
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
+1
View File
@@ -26,6 +26,7 @@ class StoreAssetRequest extends ImageUploadRequest
public function prepareForValidation(): void
{
parent::prepareForValidation(); // call ImageUploadRequest thing
// Guard against users passing in an array for company_id instead of an integer.
// If the company_id is not an integer then we simply use what was
// provided to be caught by model level validation later.
@@ -32,7 +32,7 @@ class StoreNotificationSettings extends FormRequest
],
'alert_threshold' => 'numeric|nullable',
'alert_interval' => 'numeric|nullable|gt:0',
'audit_warning_days' => 'numeric|nullable',
'audit_warning_days' => 'numeric|nullable|gte:0',
'due_checkin_days' => 'numeric|nullable|gt:0',
'audit_interval' => 'numeric|nullable|gt:0',
];
+5 -10
View File
@@ -2,6 +2,7 @@
namespace App\Http\Traits;
use Illuminate\Validation\ValidationException;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
@@ -38,20 +39,13 @@ trait ConvertsBase64ToFiles
if (!$base64Contents) {
return;
}
// autogenerate filenames
if ($filename == 'auto'){
$header = explode(';', $base64Contents, 2)[0];
// Grab the image type from the header while we're at it.
$filename = $key . '.' . substr($header, strpos($header, '/')+1);
}
// Generate a temporary path to store the Base64 contents
$tempFilePath = tempnam(sys_get_temp_dir(), $filename);
// Store the contents using a stream, or by decoding manually
// Store the contents using a stream, or throw an Error (which doesn't do anything?)
if (Str::startsWith($base64Contents, 'data:') && count(explode(',', $base64Contents)) > 1) {
$source = fopen($base64Contents, 'r');
$source = fopen($base64Contents, 'r'); // PHP has special processing for "data:" URL's
$destination = fopen($tempFilePath, 'w');
stream_copy_to_stream($source, $destination);
@@ -59,7 +53,8 @@ trait ConvertsBase64ToFiles
fclose($source);
fclose($destination);
} else {
file_put_contents($tempFilePath, base64_decode($base64Contents, true));
// TODO - to get a better error message here, can we maybe do something with modifying the errorBag?
throw new ValidationException("Need Base64 URL starting with 'data:'"); // This doesn't actually throw?
}
$uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true);
@@ -149,6 +149,7 @@ class ActionlogsTransformer
'filename' => $actionlog->filename,
'inlineable' => StorageHelper::allowSafeInline($actionlog->uploads_file_path()),
'exists_on_disk' => Storage::exists($actionlog->uploads_file_path()) ? true : false,
'mediatype' => StorageHelper::getMediaType($actionlog->uploads_file_path()),
] : null,
'item' => ($actionlog->item) ? [
@@ -48,12 +48,15 @@ class AssetModelsTransformer
'image' => ($assetmodel->image != '') ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null,
'model_number' => ($assetmodel->model_number ? e($assetmodel->model_number): null),
'min_amt' => ($assetmodel->min_amt) ? (int) $assetmodel->min_amt : null,
'remaining' => (int) ($assetmodel->assets_count - $assetmodel->min_amt),
'depreciation' => ($assetmodel->depreciation) ? [
'id' => (int) $assetmodel->depreciation->id,
'name'=> e($assetmodel->depreciation->name),
] : null,
'assets_count' => (int) $assetmodel->assets_count,
'assets_assigned_count' => (int) $assetmodel->assets_assigned_count,
'assets_archived_count' => (int) $assetmodel->assets_archived_count,
'remaining' => (int) ($assetmodel->assets_count - (int) $assetmodel->assets_assigned_count) - (int) $assetmodel->assets_archived_count,
'category' => ($assetmodel->category) ? [
'id' => (int) $assetmodel->category->id,
'name'=> e($assetmodel->category->name),
@@ -65,7 +68,7 @@ class AssetModelsTransformer
'default_fieldset_values' => $default_field_values,
'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None',
'requestable' => ($assetmodel->requestable == '1') ? true : false,
'require_serial' => $assetmodel->require_serial,
'require_serial' => ($assetmodel->require_serial == '1') ? true : false,
'notes' => Helper::parseEscapedMarkedownInline($assetmodel->notes),
'created_by' => ($assetmodel->adminuser) ? [
'id' => (int) $assetmodel->adminuser->id,
@@ -66,6 +66,7 @@ class MaintenancesTransformer
'id' => $assetmaintenance->supplier->id,
'name'=> e($assetmaintenance->supplier->name)
] : null,
'url' => ($assetmaintenance->url) ? e($assetmaintenance->url) : null,
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
+1 -1
View File
@@ -44,7 +44,7 @@ class AssetImporter extends ItemImporter
foreach ($this->customFields as $customField) {
$customFieldValue = $this->array_smart_custom_field_fetch($row, $customField);
if ($customFieldValue) {
if (!is_null($customFieldValue)) {
if ($customField->field_encrypted == 1) {
$this->item['custom_fields'][$customField->db_column_name()] = Crypt::encrypt($customFieldValue);
$this->log('Custom Field '.$customField->name.': '.Crypt::encrypt($customFieldValue));
+1 -1
View File
@@ -87,7 +87,7 @@ class CheckoutAssetMail extends Mailable
$name = $this->target->assignedto?->display_name;
}
else if($this->target instanceof Location){
$name = $this->target->manager->name;
$name = $this->target->manager?->name;
}
// Check if the item has custom fields associated with it
+4 -2
View File
@@ -17,10 +17,11 @@ class SendUpcomingAuditMail extends Mailable
/**
* Create a new message instance.
*/
public function __construct($params, $threshold)
public function __construct($params, $threshold, $total)
{
$this->assets = $params;
$this->threshold = $threshold;
$this->total = $total;
}
/**
@@ -32,7 +33,7 @@ class SendUpcomingAuditMail extends Mailable
return new Envelope(
from: $from,
subject: trans_choice('mail.upcoming-audits', $this->assets->count(), ['count' => $this->assets->count(), 'threshold' => $this->threshold]),
subject: trans_choice('mail.upcoming-audits', $this->total, ['count' => $this->total, 'threshold' => $this->threshold]),
);
}
@@ -49,6 +50,7 @@ class SendUpcomingAuditMail extends Mailable
with: [
'assets' => $this->assets,
'threshold' => $this->threshold,
'total' => $this->total,
],
);
}
+85
View File
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -390,7 +391,91 @@ class Accessory extends SnipeModel
* BEGIN QUERY SCOPES
* -----------------------------------------------
**/
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'name') {
$query->where('accessories.name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'notes') {
$query->where('accessories.notes', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'model_number') {
$query->where('accessories.model_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'order_number') {
$query->where('accessories.order_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'purchase_cost') {
$query->where('accessories.purchase_cost', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'location') {
$query->whereHas(
'location', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'manufacturer') {
$query->whereHas(
'manufacturer', function ($query) use ($search_val) {
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'supplier') {
$query->whereHas(
'supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'category') {
$query->whereHas(
'category', function ($query) use ($search_val) {
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'company') {
$query->whereHas(
'company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
}
);
}
}
}
);
}
/**
* Query builder scope to order on created_by name
+8
View File
@@ -478,6 +478,10 @@ class Actionlog extends SnipeModel
$object = 'models';
}
if ($this->action_type == 'audit') {
$object = 'audits';
}
return route('ui.files.show', [
'object_type' => $object,
'id' => $this->item_id,
@@ -493,6 +497,10 @@ class Actionlog extends SnipeModel
return 'private_uploads/eula-pdfs/'.$this->filename;
}
if ($this->action_type == 'audit') {
return 'private_uploads/audits/'.$this->filename;
}
switch ($this->item_type) {
case Accessory::class:
return 'private_uploads/accessories/'.$this->filename;
+200 -65
View File
@@ -9,6 +9,8 @@ use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\Acceptable;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Requestable;
use App\Models\Traits\Searchable;
use App\Presenters\AssetPresenter;
use App\Presenters\Presentable;
@@ -227,7 +229,6 @@ class Asset extends Depreciable
}
public function customFieldValidationRules()
{
@@ -266,7 +267,6 @@ class Asset extends Depreciable
return parent::save($params);
}
public function getDisplayNameAttribute()
{
return $this->present()->name();
@@ -277,22 +277,136 @@ class Asset extends Depreciable
*
* @return \Carbon\Carbon|null
*/
public function getWarrantyExpiresAttribute()
protected function warrantyExpires(): Attribute
{
if (isset($this->attributes['warranty_months']) && isset($this->attributes['purchase_date'])) {
if (is_string($this->attributes['purchase_date']) || is_string($this->attributes['purchase_date'])) {
$purchase_date = \Carbon\Carbon::parse($this->attributes['purchase_date']);
} else {
$purchase_date = \Carbon\Carbon::instance($this->attributes['purchase_date']);
}
$purchase_date->setTime(0, 0, 0);
return $purchase_date->addMonths((int) $this->attributes['warranty_months']);
}
return null;
return Attribute:: make(
get: fn(mixed $value, array $attributes) => ($attributes['warranty_months'] && $attributes['purchase_date']) ? Carbon::parse($attributes['purchase_date'])->addMonths($attributes['warranty_months']) : null,
);
}
protected function warrantyExpiresFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->warrantyExpires, 'date', false)
);
}
protected function warrantyExpiresDiff(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? round((Carbon::now()->diffInDays($this->warrantyExpires))) : null,
);
}
protected function warrantyExpiresDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? Carbon::parse($this->warrantyExpires)->diffForHumans() : null,
);
}
protected function lastAuditFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->last_audit_date, 'datetime', false)
);
}
protected function lastAuditDiff(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? round((Carbon::now()->diffInDays($this->warrantyExpires))) : null,
);
}
protected function lastAuditDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['last_audit_date'] ? Carbon::parse($attributes['last_audit_date'])->diffForHumans() : null,
);
}
protected function nextAuditFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->next_audit_date, 'date', false)
);
}
protected function nextAuditDiffInDays(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['next_audit_date'] ? Carbon::now()->diffInDays($attributes['next_audit_date']) : null,
);
}
protected function nextAuditDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['next_audit_date'] ? Carbon::parse($attributes['next_audit_date'])->diffForHumans() : null,
);
}
protected function eolDate(): Attribute
{
return Attribute:: make(
get: function(mixed $value, array $attributes) {
if ($attributes['asset_eol_date'] && $attributes['eol_explicit'] == '1') {
return Carbon::parse($attributes['asset_eol_date']);
} elseif ($attributes['purchase_date'] && $this->model && ((int) $this->model->eol > 0)) {
return Carbon::parse($attributes['purchase_date'])->addMonths((int) $this->model->eol);
} else {
return null;
}
}
);
}
protected function eolFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->eolDate ? Helper::getFormattedDateObject($this->eolDate, 'date', false) : null,
);
}
protected function eolDiffInDays(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->eolDate ? round((Carbon::now()->diffInDays(Carbon::parse($this->eolDate), false, 1))) : null,
);
}
protected function eolDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $this->eolDate ? Carbon::parse($this->eolDate)->diffForHumans() : null,
);
}
protected function expectedCheckinFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Helper::getFormattedDateObject($attributes['expected_checkin'], 'date', false) : null,
);
}
/**
* Establishes the asset -> company relationship
@@ -825,25 +939,37 @@ class Asset extends Depreciable
*/
public static function getExpiringWarrantyOrEol($days = 30)
{
return self::where('archived', '=', '0')
$now = now();
$end = now()->addDays($days);
$expired_assets = self::query()
->where('archived', '=', '0')
->NotArchived()
->whereNull('deleted_at')
->where(function ($query) use ($days) {
// Check for manual asset EOL first
$query->where(function ($query) use ($days) {
$query->whereNotNull('asset_eol_date')
->whereBetween('asset_eol_date', [Carbon::now(), Carbon::now()->addDays($days)]);
// Otherwise use the warranty months + purchase date + threshold
})->orWhere(function ($query) use ($days) {
$query->whereNotNull('purchase_date')
->whereNotNull('warranty_months')
->whereBetween('purchase_date', [Carbon::now(), Carbon::now()->addMonths('assets.warranty_months')->addDays($days)]);
});
})
->orderBy('asset_eol_date', 'ASC')
->orderBy('purchase_date', 'ASC')
->whereNotNull('asset_eol_date')
->whereBetween('asset_eol_date', [$now, $end])
->get();
$assets_with_warranties = self::query()
->where('archived', '=', '0')
->NotArchived()
->whereNull('deleted_at')
->whereNotNull('purchase_date')
->whereNotNull('warranty_months')
->get();
$expired_warranties = $assets_with_warranties->filter(function ($asset) use ($now, $end) {
$expiration_window = Carbon::parse($asset->purchase_date)->addMonths((int) $asset->warranty_months);
return $expiration_window->betweenIncluded($now, $end);
});
return $expired_assets->concat($expired_warranties)
->unique('id')
->sortBy([
['asset_eol_date', 'ASC'],
['purchase_date', 'ASC']
])
->values();
}
@@ -1775,16 +1901,32 @@ class Asset extends Depreciable
);
}
if ($fieldname =='assigned_to') {
if ($fieldname == 'assigned_to') {
$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.'%');
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
}
);
}
)->orWhereHasMorph(
'assignedTo', [Location::class], function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
}
)->orWhereHasMorph(
'assignedTo', [Asset::class], function ($query) use ($search_val) {
$query->where(
function ($query) use ($search_val) {
// Don't use the asset table prefix here because it will pull from the original asset,
// not the subselect we're doing here to get the assigned asset
$query->where('name', 'LIKE', '%'.$search_val.'%')
->orWhere('asset_tag', 'LIKE', '%'.$search_val.'%');
}
);
}
);
}
@@ -1824,51 +1966,44 @@ class Asset extends Depreciable
}
if ($fieldname == 'model') {
$query->where(
function ($query) use ($search_val) {
$query->whereHas(
'model', function ($query) use ($search_val) {
$query->where('models.name', 'LIKE', '%'.$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('models.model_number', 'LIKE', '%'.$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('companies.name', 'LIKE', '%'.$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('suppliers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
$query->whereHas(
'supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'status_label') {
$query->whereHas(
'assetstatus', function ($query) use ($search_val) {
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
}
);
}
+71 -2
View File
@@ -2,16 +2,18 @@
namespace App\Models;
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Requestable;
use App\Models\Traits\Searchable;
use App\Presenters\AssetModelPresenter;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Watson\Validating\ValidatingTrait;
use \App\Presenters\AssetModelPresenter;
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
/**
* Model for Asset Models. Asset Models contain higher level
@@ -122,6 +124,22 @@ class AssetModel extends SnipeModel
return $this->hasMany(\App\Models\Asset::class, 'model_id');
}
public function availableAssets()
{
return $this->hasMany(\App\Models\Asset::class, 'model_id')->RTD();
}
public function assignedAssets()
{
return $this->hasMany(\App\Models\Asset::class, 'model_id')->Deployed();
}
public function archivedAssets()
{
return $this->hasMany(\App\Models\Asset::class, 'model_id')->Archived();
}
/**
* Establishes the model -> category relationship
*
@@ -240,6 +258,57 @@ class AssetModel extends SnipeModel
* -----------------------------------------------
**/
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'name') {
$query->where('models.name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'notes') {
$query->where('models.notes', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'model_number') {
$query->where('models.model_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'category') {
$query->whereHas(
'category', function ($query) use ($search_val) {
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'manufacturer') {
$query->whereHas(
'manufacturer', function ($query) use ($search_val) {
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
}
}
);
}
/**
* scopeInCategory
* Get all models that are in the array of category ids
+29
View File
@@ -290,6 +290,35 @@ class Category extends SnipeModel
* -----------------------------------------------
**/
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'name') {
$query->where('categories.name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'category_type') {
$query->where('categories.category_type', 'LIKE', '%' . $search_val . '%');
}
}
}
);
}
/**
* Query builder scope for whether or not the category requires acceptance
*
+15 -5
View File
@@ -184,12 +184,17 @@ class CheckoutAcceptance extends Model
$pdf->AddPage();
if ($data['logo'] != null) {
$pdf->writeHTML('<img src="'.$data['logo'].'">', true, 0, true, 0, '');
$pdf->writeHTML('<img src="@'.$data['logo'].'">', true, 0, true, 0, '');
} else {
$pdf->writeHTML('<h3>'.$data['site_name'].'</h3><br /><br />', true, 0, true, 0, 'C');
}
$pdf->Ln();
// Check for CJK in the translation string for date. (This is a good proxy for the rest of the document)
Helper::hasRtl(trans('general.date')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
Helper::isCjk(trans('general.date')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
$pdf->writeHTML(trans('general.date') . ': ' . Helper::getFormattedDateObject(now(), 'datetime', false), true, 0, true, 0, '');
if ($data['company_name'] != null) {
@@ -210,7 +215,10 @@ class CheckoutAcceptance extends Model
if (($data['qty'] != null) && ($data['qty'] > 1)) {
$pdf->writeHTML(trans('general.qty').': '.e($data['qty']), true, 0, true, 0, '');
}
$pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']), true, 0, true, 0, '');
$pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']) . ($data['employee_num'] ? ' ('.$data['employee_num'].')' : ''), true, 0, true, 0, '');
if ($data['email'] != null) {
$pdf->writeHTML(trans('general.email').': '.e($data['email']), true, 0, true, 0, '');
}
$pdf->Ln();
$pdf->writeHTML('<hr>', true, 0, true, 0, '');
@@ -221,7 +229,6 @@ class CheckoutAcceptance extends Model
foreach ($eula_lines as $eula_line) {
Helper::hasRtl($eula_line) ? $pdf->setRTL(true) : $pdf->setRTL(false);
Helper::isCjk($eula_line) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
$pdf->writeHTML(Helper::parseEscapedMarkedown($eula_line), true, 0, true, 0, '');
}
$pdf->Ln();
@@ -230,14 +237,17 @@ class CheckoutAcceptance extends Model
$pdf->Ln();
if ($data['signature'] != null) {
$pdf->writeHTML('<img src="'.$data['signature'].'">', true, 0, true, 0, '');
$pdf->writeHTML('<img src="@'.$data['signature'].'">', true, 0, true, 0, '');
$pdf->writeHTML('<hr>', true, 0, true, 0, '');
$pdf->writeHTML(e($data['assigned_to']), true, 0, true, 0, 'C');
$pdf->Ln();
}
Helper::hasRtl(trans('general.notes')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
if ($data['note'] != null) {
Helper::isCjk($data['note']) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
$pdf->writeHTML(trans('general.notes') . ': ' . e($data['note']), true, 0, true, 0, '');
$pdf->Ln();
}
+92
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -321,6 +322,97 @@ class Component extends SnipeModel
* -----------------------------------------------
**/
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'name') {
$query->where('components.name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'notes') {
$query->where('components.notes', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'model_number') {
$query->where('components.model_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'order_number') {
$query->where('components.order_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'serial') {
$query->where('components.serial', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'serial') {
$query->where('components.serial', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'purchase_cost') {
$query->where('components.purchase_cost', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'location') {
$query->whereHas(
'location', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'manufacturer') {
$query->whereHas(
'manufacturer', function ($query) use ($search_val) {
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'supplier') {
$query->whereHas(
'supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'category') {
$query->whereHas(
'category', function ($query) use ($search_val) {
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'company') {
$query->whereHas(
'company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
}
);
}
}
}
);
}
/**
* Query builder scope to order on company
+94
View File
@@ -6,6 +6,7 @@ use App\Helpers\Helper;
use App\Models\Traits\Acceptable;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\ConsumablePresenter;
use App\Presenters\Presentable;
@@ -345,6 +346,99 @@ class Consumable extends SnipeModel
* -----------------------------------------------
**/
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'name') {
$query->where('consumables.name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'notes') {
$query->where('consumables.notes', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'model_number') {
$query->where('consumables.model_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'order_number') {
$query->where('consumables.order_number', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'item_no') {
$query->where('consumables.item_no', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'serial') {
$query->where('consumables.serial', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'purchase_cost') {
$query->where('consumables.purchase_cost', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'location') {
$query->whereHas(
'location', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'manufacturer') {
$query->whereHas(
'manufacturer', function ($query) use ($search_val) {
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'supplier') {
$query->whereHas(
'supplier', function ($query) use ($search_val) {
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'category') {
$query->whereHas(
'category', function ($query) use ($search_val) {
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
}
);
}
if ($fieldname == 'company') {
$query->whereHas(
'company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
}
);
}
}
}
);
}
/**
* Query builder scope to order on company
*
+3 -2
View File
@@ -229,7 +229,7 @@ class DefaultLabel extends RectangleSheet
static::writeText(
$pdf, $record->get('title'),
$textX1, 0,
'freesans', 'b', $this->textSize, 'L',
Helper::isCjk($record->get('title')) ? 'cid0cs' : 'freesans', 'b', $this->textSize, 'L',
$textW, $this->textSize,
true, 0
);
@@ -246,11 +246,12 @@ class DefaultLabel extends RectangleSheet
static::writeText(
$pdf, (($field['label']) ? $field['label'].' ' : '') . $field['value'],
$textX1, $textY,
'freesans', '', $this->textSize, 'L',
Helper::isCjk($field['label']) ? 'cid0cs' : 'freesans', '', $this->textSize, 'L',
$textW, $this->textSize,
true, 0
);
$textY += $this->textSize + self::TEXT_MARGIN;
$fieldsDone++;
}
+11 -4
View File
@@ -211,29 +211,36 @@ abstract class Label
*/
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();
$text = !empty($text) ? $text : '';
$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) {
function ($part, $index) use ($pdf, $fontFamily, $fontStyle, $fontSizePt, $text) {
$modStyle = ($index % 2 == 1) ? 'B' : $fontStyle;
$pdf->setFont($fontFamily, $modStyle, $fontSizePt);
return [
'text' => $part,
'text_width' => $pdf->GetStringWidth($part),
'font_family' => $fontFamily,
'font_family' => Helper::isCjk($text) ? 'cid0cs' : $fontFamily,
'font_style' => $modStyle,
'font_size' => $fontSizePt,
];
@@ -0,0 +1,107 @@
<?php
namespace App\Models\Labels\Tapes\Brother;
class TZe_24mm_E extends TZe_24mm
{
private const BARCODE_MARGIN = 1.50;
private const TAG_SIZE = 2.00;
private const TITLE_SIZE = 2.80;
private const TITLE_MARGIN = 0.50;
private const LABEL_SIZE = 2.00;
private const LABEL_MARGIN = - 0.35;
private const FIELD_SIZE = 2.80;
private const FIELD_MARGIN = 0.15;
private const BARCODE1D_SIZE = - 1.00;
public function getUnit() { return 'mm'; }
public function getWidth() { return 45.0; }
public function getHeight() { return 15; }
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 write($pdf, $record) {
$pa = $this->getPrintableArea();
$currentX = $pa->x1;
$currentY = $pa->y1;
$usableWidth = $pa->w;
$usableHeight = $pa->h - self::BARCODE1D_SIZE;
$barcodeSize = $usableHeight - self::TAG_SIZE;
if ($record->has('barcode2d')) {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
'freesans', 'b', self::TAG_SIZE, 'C',
$barcodeSize, self::TAG_SIZE, true, 0
);
static::write2DBarcode(
$pdf, $record->get('barcode2d')->content, $record->get('barcode2d')->type,
$currentX, $currentY,
$barcodeSize, $barcodeSize
);
$currentX += $barcodeSize + self::BARCODE_MARGIN;
$usableWidth -= $barcodeSize + self::BARCODE_MARGIN;
} else {
static::writeText(
$pdf, $record->get('tag'),
$pa->x1, $pa->y2 - self::TAG_SIZE - self::BARCODE1D_SIZE,
'freesans', 'B', self::TAG_SIZE, 'R',
$usableWidth, self::TAG_SIZE, true, 0
);
}
if ($record->has('title')) {
static::writeText(
$pdf, $record->get('title'),
$currentX, $currentY,
'freesans', 'B', self::TITLE_SIZE, 'L',
$usableWidth, self::TITLE_SIZE, true, 0
);
$currentY += self::TITLE_SIZE + self::TITLE_MARGIN;
}
foreach ($record->get('fields') as $field) {
// Write label and value on the same line
// Calculate label width with proportional character spacing
$labelWidth = $pdf->GetStringWidth($field['label'], 'freesans', '', self::LABEL_SIZE);
$charCount = strlen($field['label']);
$spacingPerChar = 0.5;
$totalSpacing = $charCount * $spacingPerChar;
$adjustedWidth = $labelWidth + $totalSpacing;
static::writeText(
$pdf, $field['label'],
$currentX, $currentY,
'freesans', 'B', self::LABEL_SIZE, 'L',
$adjustedWidth, self::LABEL_SIZE, true, 0, $spacingPerChar
);
static::writeText(
$pdf, $field['value'],
$currentX + $adjustedWidth + 2, $currentY,
'freesans', 'B', self::FIELD_SIZE, 'L',
$usableWidth - $adjustedWidth - 2, self::FIELD_SIZE, true, 0, 0.3
);
$currentY += max(self::LABEL_SIZE, self::FIELD_SIZE) + self::FIELD_MARGIN;
}
if ($record->has('barcode1d')) {
static::write1DBarcode(
$pdf, $record->get('barcode1d')->content, $record->get('barcode1d')->type,
$currentX, $barcodeSize + self::BARCODE_MARGIN, $usableWidth - self::TAG_SIZE, self::TAG_SIZE
);
}
}
}
+90 -16
View File
@@ -104,6 +104,78 @@ class Ldap extends Model
return $connection;
}
/**
* Finds user via Admin search *first*, and _then_ try to bind as that user, returning the user attributes on success,
* or false on failure. This enables login when the DN is harder to programmatically 'guess' due to having users in
* various different OU's or other LDAP entities.
*/
public static function findAndBindMultiOU(string $baseDn, string $filterQuery, string $password, int $slow_failure = 3): array|false
{
/**
* If you *don't* set the slow_failure variable, do note that we might permit timing attacks in here - if
* your find results come back 'slow' when a user *does* exist, but fast if they *don't* exist, then you
* can use this to enumerate users.
*
* Even if that's *not* true, we still might have an issue: if we don't find the user, then we don't even _try_
* to bind as them. Again, that could permit a timing attack.
*
* Instead of checking every little thing, we just wrap everything in a try/catch in order to unify the
* 'slow_failure' treatment. All failures are re-raised as exceptions so that all failures exit from the
* same place.
*/
$connection = null;
$admin_conn = null;
try {
/**
* First we get an 'admin' connection, which will need search permissions. That was already a requirement
* here, so that's not a big lift. But it _is_ possible to configure LDAP to only login, and *not* to be
* able to import lists of users. In that case, this function *will not work* - and you should use the
* legacy 'findAndBindUserLdap' method, below. Otherwise, it looks like this would attempt an anonymous
* bind - which you might want, but you probably don't.
*
**/
$admin_conn = self::connectToLdap();
self::bindAdminToLdap($admin_conn);
$results = ldap_search($admin_conn, $baseDn, $filterQuery);
$entry_count = ldap_count_entries($admin_conn, $results);
if ($entry_count != 1) {
throw new \Exception('Wrong number of entries found: ' . $entry_count);
}
$entry = ldap_first_entry($admin_conn, $results);
$user = ldap_get_attributes($admin_conn, $entry);
$userDn = ldap_get_dn($admin_conn, $entry);
if (!$userDn) {
throw new \Exception("No user DN found");
}
\Log::debug("FOUND DN IS: $userDn");
// The temptation now is to do ldap_unbind on the $admin_conn, but that gets handled in the 'finally' below.
// I don't know if that means a separate 'connection' is maintained to the LDAP server or not, and would
// definitely prefer to not do that if we can avoid it. But I don't know enough about the LDAP protocol to
// be certain that that happens.
//now we try to log in (bind) as that found user
$connection = self::connectToLdap();
$bind_results = ldap_bind($connection, $userDn, $password);
if (!$bind_results) {
throw new \Exception("Unable to bind as user");
}
return array_change_key_case($user);
} catch (\Exception $e) {
\Log::debug("Exception on fast find-and-bind: " . $e->getMessage());
if ($slow_failure) {
sleep($slow_failure);
}
return false; //TODO - make this null instead for a slightly nicer type signature
} finally {
if ($admin_conn) {
ldap_unbind($admin_conn);
}
if ($connection) {
ldap_unbind($connection);
}
}
}
/**
* Binds/authenticates the user to LDAP, and returns their attributes.
@@ -147,25 +219,27 @@ class Ldap extends Model
Log::debug('Filter query: '.$filterQuery);
// only try this if we have an Admin username set; otherwise use the 'legacy' method
if (($settings->ldap_uname) && ($baseDn)) {
// in the fallowing call, we pick a slow-failure of 0 because we might need to fall through to 'legacy'
$fast_bind = self::findAndBindMultiOU($baseDn, $filterQuery, $password, 0);
if ($fast_bind) {
\Log::debug("Fast bind worked");
return $fast_bind;
}
\Log::debug("Fast bind failed; falling through to legacy bind");
}
if (! $ldapbind = @ldap_bind($connection, $userDn, $password)) {
Log::debug("Status of binding user: $userDn to directory: (directly!) ".($ldapbind ? "success" : "FAILURE"));
if (! $ldapbind = self::bindAdminToLdap($connection)) {
/*
* TODO PLEASE:
*
* this isn't very clear, so it's important to note: the $ldapbind value is never correctly returned - we never 'return true' from self::bindAdminToLdap() (the function
* just "falls off the end" without ever explictly returning 'true')
*
* but it *does* have an interesting side-effect of checking for the LDAP password being incorrectly encrypted with the wrong APP_KEY, so I'm leaving it in for now.
*
* If it *did* correctly return 'true' on a succesful bind, it would _probably_ allow users to log in with an incorrect password. Which would be horrible!
*
* Let's definitely fix this at the next refactor!!!!
*
*/
Log::debug("Status of binding Admin user: $userDn to directory instead: ".($ldapbind ? "success" : "FAILURE"));
return false;
// replicate the old bad-decryption-key detection behavior here
try {
Crypt::decrypt(Setting::getSettings()->ldap_pword);
} catch (\Exception $e) {
throw new \Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
}
//regardless of anything else; stuff isn't working. Return false.
return false;
}
if (! $results = ldap_search($connection, $baseDn, $filterQuery)) {
+72 -52
View File
@@ -5,9 +5,11 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
@@ -156,6 +158,29 @@ class License extends Depreciable
);
}
protected function terminatesFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Helper::getFormattedDateObject($attributes['termination_date'], 'date', false) : null,
);
}
protected function terminatesDiffInDays(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::now()->diffInDays($attributes['termination_date']) : null,
);
}
protected function terminatesDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::parse($attributes['termination_date'])->diffForHumans() : null,
);
}
public function prepareLimitChangeRule($parameters, $field)
{
$actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
@@ -706,49 +731,6 @@ class License extends Depreciable
return $this->hasMany(\App\Models\LicenseSeat::class)->whereNull('assigned_to')->whereNull('deleted_at')->whereNull('asset_id');
}
/**
* Returns expiring licenses.
*
* This checks if:
*
* 1) The license has not been deleted
* 2) The expiration date is between now and the number of days specified
* 3) There is an expiration date set and the termination date has not passed
* 4) The license termination date is null or has not passed
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
* @see \App\Console\Commands\SendExpiringLicenseNotifications
*/
public static function getExpiringLicenses($days = 60)
{
return self::whereNull('licenses.deleted_at')
// The termination date is null or within range
->where(function ($query) use ($days) {
$query->whereNull('termination_date')
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
})
->where(function ($query) use ($days) {
$query->whereNotNull('expiration_date')
// Handle expired licenses without termination dates
->where(function ($query) use ($days) {
$query->whereNull('termination_date')
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
})
// Handle expired licenses with termination dates in the future
->orWhere(function ($query) use ($days) {
$query->whereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
});
})
->orderBy('expiration_date', 'ASC')
->orderBy('termination_date', 'ASC')
->get();
}
public function scopeActiveLicenses($query)
{
@@ -765,19 +747,57 @@ class License extends Depreciable
});
}
/**
* Expiried/terminated licenses scope
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
* @see \App\Console\Commands\SendExpiringLicenseNotifications
*/
public function scopeExpiredLicenses($query)
{
return $query->whereDate('termination_date', '<=', Carbon::now())// The termination date is null or within range
->orWhere(function ($query) {
$query->whereDate('expiration_date', '<=', Carbon::now());
})
->whereNull('licenses.deleted_at');
}
return $query->whereNull('licenses.deleted_at')
/**
* Expiring/terminating licenses scope
*
* This checks if:
*
* 1) The license has not been deleted
* 2) The expiration date is between now and the number of days specified
* 3) There is an expiration date set and the termination date has not passed
* 4) The license termination date is null or has not passed
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v1.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
* @see \App\Console\Commands\SendExpiringLicenseNotifications
*/
public function scopeExpiringLicenses($query, $days = 60)
{
return $query// The termination date is null or within range
->where(function ($query) use ($days) {
$query->whereNull('termination_date')
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
})
->where(function ($query) use ($days) {
$query->whereNotNull('expiration_date')
// Handle expiring licenses without termination dates
->where(function ($query) use ($days) {
$query->whereNull('termination_date')
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
})
// The termination date is null or within range
->where(function ($query) {
$query->whereNull('termination_date')
->orWhereDate('termination_date', '<=', [Carbon::now()]);
})
->orWhere(function ($query) {
$query->whereNull('expiration_date')
->orWhereDate('expiration_date', '<=', [Carbon::now()]);
// Handle expiring licenses with termination dates in the future
->orWhere(function ($query) use ($days) {
$query->whereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
});
});
}
+2 -1
View File
@@ -4,6 +4,7 @@ namespace App\Models;
use App\Models\Traits\Acceptable;
use App\Models\Traits\CompanyableChildTrait;
use App\Models\Traits\Loggable;
use App\Notifications\CheckinLicenseNotification;
use App\Notifications\CheckoutLicenseNotification;
use App\Presenters\Presentable;
@@ -75,7 +76,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
protected function displayName(): Attribute
{
return Attribute:: make(
get: fn(mixed $value) => $this->license->name,
get: fn(mixed $value) => $this->license?->name,
);
}
+1
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
+3
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Helpers\Helper;
use App\Models\Traits\CompanyableChildTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -38,6 +39,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
'completion_date' => 'date_format:Y-m-d|nullable|after_or_equal:start_date',
'notes' => 'string|nullable',
'cost' => 'numeric|nullable|gte:0|max:99999999999999999.99',
'url' => 'nullable|url|max:255',
];
@@ -57,6 +59,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
'asset_maintenance_time',
'notes',
'cost',
'url',
];
use Searchable;
+40
View File
@@ -3,6 +3,7 @@
namespace App\Models;
use App\Helpers\Helper;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
@@ -19,6 +20,44 @@ class SnipeModel extends Model
$this->attributes['purchase_date'] = $value;
}
protected function purchaseDateForDatepicker(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => array_key_exists('purchase_date', $attributes) ? Carbon::parse($attributes['purchase_date'])->format('Y-m-d') : null,
);
}
protected function purchaseDateFormatted(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => $attributes['purchase_date'] ? Helper::getFormattedDateObject(Carbon::parse($attributes['purchase_date']), 'date', false) : null,
);
}
protected function expiresDiffInDays(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::now()->diffInDays($attributes['expiration_date']) : null,
);
}
protected function expiresDiffForHumans(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::parse($attributes['expiration_date'])->diffForHumans() : null,
);
}
protected function expiresFormattedDate(): Attribute
{
return Attribute:: make(
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Helper::getFormattedDateObject($attributes['expiration_date'], 'date', false) : null,
);
}
/**
* @param $value
*/
@@ -180,6 +219,7 @@ class SnipeModel extends Model
);
}
public function getEula()
{
@@ -1,8 +1,14 @@
<?php
namespace App\Models;
namespace App\Models\Traits;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\AuditNotification;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
@@ -1,8 +1,10 @@
<?php
namespace App\Models;
namespace App\Models\Traits;
use Illuminate\Support\Facades\Auth;
use App\Models\CheckoutRequest;
use App\Models\User;
// $asset->requests
// $asset->isRequestedBy($user)
+156
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Http\Traits\UniqueUndeletedTrait;
use App\Models\Traits\CompanyableTrait;
use App\Models\Traits\HasUploads;
use App\Models\Traits\Loggable;
use App\Models\Traits\Searchable;
use App\Presenters\Presentable;
use App\Presenters\UserPresenter;
@@ -224,6 +225,26 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return false;
}
public function hasIndividualPermissions()
{
$permissions = [];
if (is_object($this->permissions)) {
$permissions = json_decode(json_encode($this->permissions), true);
}
if (is_string($this->permissions)) {
$permissions = json_decode($this->permissions, true);
}
foreach ($permissions as $permission) {
if ($permission != 0) {
return true;
}
}
return false;
}
/**
* Internally check the user permission for the given section
*
@@ -863,6 +884,141 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
return new \stdClass;
}
/**
* Query builder scope to search on text filters for complex Bootstrap Tables API
*
* @param \Illuminate\Database\Query\Builder $query Query builder instance
* @param text $filter JSON array of search keys and terms
*
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeByFilter($query, $filter)
{
return $query->where(
function ($query) use ($filter) {
foreach ($filter as $fieldname => $search_val) {
if ($fieldname == 'first_name') {
$query->where('users.first_name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'last_name') {
$query->where('users.last_name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'display_name') {
$query->where('users.display_name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'name') {
$query->where('users.last_name', 'LIKE', '%' . $search_val . '%')
->orWhere('users.first_name', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'username') {
$query->where('users.username', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'email') {
$query->where('users.email', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'phone') {
$query->where('users.phone', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'mobile') {
$query->where('users.mobile', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'phone') {
$query->where('users.phone', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'jobtitle') {
$query->where('users.jobtitle', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'created_at') {
$query->where('users.created_at', '=', '%' . $search_val . '%');
}
if ($fieldname == 'updated_at') {
$query->where('users.updated_at', '=', '%' . $search_val . '%');
}
if ($fieldname == 'start_date') {
$query->where('users.start_date', '=', '%' . $search_val . '%');
}
if ($fieldname == 'end_date') {
$query->where('users.end_date', '=', '%' . $search_val . '%');
}
if ($fieldname == 'employee_num') {
$query->where('users.employee_num', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'locale') {
$query->where('users.locale', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'address') {
$query->where('users.address', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'state') {
$query->where('users.state', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'zip') {
$query->where('users.zip', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'country') {
$query->where('users.country', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'vip') {
$query->where('users.vip', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'remote') {
$query->where('users.remote', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'start_date') {
$query->where('users.purchase_date', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'notes') {
$query->where('users.notes', 'LIKE', '%' . $search_val . '%');
}
if ($fieldname == 'location') {
$query->whereHas(
'location', function ($query) use ($search_val) {
$query->where('locations.name', 'LIKE', '%' . $search_val . '%');
}
);
}
if ($fieldname == 'company') {
$query->whereHas(
'company', function ($query) use ($search_val) {
$query->where('companies.name', 'LIKE', '%' . $search_val . '%');
}
);
}
}
}
);
}
/**
* Query builder scope to search user by name with spaces in it.
* We don't use the advancedTextSearch() scope because that searches
@@ -34,7 +34,6 @@ use Illuminate\Notifications\Notification;
$this->file = $params['file'] ?? null;
$this->qty = $params['qty'] ?? null;
$this->note = $params['note'] ?? null;
$this->admin = $params['admin'] ?? null;
}
@@ -77,7 +76,6 @@ use Illuminate\Notifications\Notification;
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'admin' => $this->admin,
'qty' => $this->qty,
'intro_text' => trans('mail.acceptance_asset_accepted'),
])
@@ -32,7 +32,6 @@ use Illuminate\Notifications\Notification;
$this->settings = Setting::getSettings();
$this->file = $params['file'] ?? null;
$this->qty = $params['qty'] ?? null;
$this->admin = $params['admin'] ?? null;
}
/**
@@ -70,7 +69,6 @@ use Illuminate\Notifications\Notification;
'accepted_date' => $this->accepted_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'admin' => $this->admin,
'qty' => $this->qty,
'intro_text' => trans_choice('mail.acceptance_asset_accepted_to_user', $this->qty, ['qty' => $this->qty, 'site_name' => $this->settings->site_name]),
])
@@ -31,6 +31,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
$this->company_name = $params['company_name'];
$this->settings = Setting::getSettings();
$this->qty = $params['qty'] ?? null;
$this->admin = $params['admin'] ?? null;
}
/**
@@ -70,7 +71,8 @@ class AcceptanceAssetDeclinedNotification extends Notification
'declined_date' => $this->declined_date,
'assigned_to' => $this->assigned_to,
'company_name' => $this->company_name,
'qty' => $this->qty,
'qty' => $this->qty,
'admin' => $this->admin,
'intro_text' => trans('mail.acceptance_asset_declined'),
])
->subject(trans('mail.acceptance_asset_declined'));
@@ -93,8 +93,8 @@ class CheckoutAssetNotification extends Notification
$channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : '';
$fields = [
trans('general.to') => '<'.$target->present()->viewUrl().'|'.$target->display_name.'>',
trans('general.by') => '<'.$admin->present()->viewUrl().'|'.$admin->display_name.'>',
trans('general.to_user') => '<'.$target->present()->viewUrl().'|'.$target->display_name.'>',
trans('general.by_user') => '<'.$admin->present()->viewUrl().'|'.$admin->display_name.'>',
];
if ($item->location) {
+4 -4
View File
@@ -168,14 +168,14 @@ class AssetObserver
public function saving(Asset $asset)
{
// determine if calculated eol and then calculate it - this should only happen on a new asset
if (is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && ($asset->model->eol > 0)){
if (is_null($asset->asset_eol_date) && !is_null($asset->purchase_date) && ($asset->model?->eol > 0)) {
$asset->asset_eol_date = $asset->purchase_date->addMonths($asset->model->eol)->format('Y-m-d');
$asset->eol_explicit = false;
}
// determine if explicit and set eol_explicit to true
if (!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) {
if($asset->model->eol > 0) {
if ($asset->model?->eol > 0) {
$months = (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true);
if($months != $asset->model->eol) {
$asset->eol_explicit = true;
@@ -184,9 +184,9 @@ class AssetObserver
} elseif (!is_null($asset->asset_eol_date) && is_null($asset->purchase_date)) {
$asset->eol_explicit = true;
}
if ((!is_null($asset->asset_eol_date)) && (!is_null($asset->purchase_date)) && (is_null($asset->model->eol) || ($asset->model->eol == 0))) {
if ((!is_null($asset->asset_eol_date)) && (!is_null($asset->purchase_date)) && (is_null($asset->model?->eol) || ($asset->model?->eol == 0))) {
$asset->eol_explicit = true;
}
}
}
+24 -5
View File
@@ -53,7 +53,7 @@ class AssetModelPresenter extends Presenter
],
[
'field' => 'manufacturer',
'searchable' => false,
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.manufacturer'),
@@ -62,7 +62,7 @@ class AssetModelPresenter extends Presenter
],
[
'field' => 'model_number',
'searchable' => false,
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/models/table.modelnumber'),
@@ -89,17 +89,36 @@ class AssetModelPresenter extends Presenter
'class' => 'text-right text-padding-number-cell',
'footerFormatter' => 'qtySumFormatter',
],
[
'field' => 'assets_assigned_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.assigned'),
'visible' => true,
'class' => 'text-right text-padding-number-cell',
'footerFormatter' => 'qtySumFormatter',
],
[
'field' => 'remaining',
'searchable' => false,
'sortable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.remaining'),
'visible' => true,
'class' => 'text-right text-padding-number-cell',
'footerFormatter' => 'qtySumFormatter',
],
[
'field' => 'assets_archived_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.archived'),
'visible' => true,
'class' => 'text-right text-padding-number-cell',
'footerFormatter' => 'qtySumFormatter',
],
[
'field' => 'depreciation',
'searchable' => false,
@@ -111,7 +130,7 @@ class AssetModelPresenter extends Presenter
],
[
'field' => 'category',
'searchable' => false,
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.category'),
+7 -2
View File
@@ -14,6 +14,11 @@ class CategoryPresenter extends Presenter
public static function dataTableLayout()
{
$layout = [
[
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
],
[
'field' => 'id',
'searchable' => false,
@@ -92,7 +97,7 @@ class CategoryPresenter extends Presenter
'formatter' => 'usersLinkObjFormatter',
], [
'field' => 'created_at',
'searchable' => true,
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.created_at'),
@@ -100,7 +105,7 @@ class CategoryPresenter extends Presenter
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'updated_at',
'searchable' => true,
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.updated_at'),
+1 -1
View File
@@ -131,7 +131,7 @@ class ComponentPresenter extends Presenter
'class' => 'text-right',
], [
'field' => 'total_cost',
'searchable' => true,
'searchable' => false,
'sortable' => true,
'title' => trans('general.total_cost'),
'footerFormatter' => 'sumFormatterQuantity',
+7 -8
View File
@@ -48,6 +48,13 @@ class LicensePresenter extends Presenter
'sortable' => true,
'title' => trans('admin/licenses/form.expiration'),
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'termination_date',
'searchable' => true,
'sortable' => true,
'visible' => false,
'title' => trans('admin/licenses/form.termination_date'),
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'license_email',
'searchable' => true,
@@ -110,14 +117,6 @@ class LicensePresenter extends Presenter
'title' => trans('general.purchase_date'),
'formatter' => 'dateDisplayFormatter',
],
[
'field' => 'termination_date',
'searchable' => true,
'sortable' => true,
'visible' => false,
'title' => trans('admin/licenses/form.termination_date'),
'formatter' => 'dateDisplayFormatter',
],
[
'field' => 'depreciation',
'searchable' => true,
+6
View File
@@ -111,6 +111,12 @@ class MaintenancesPresenter extends Presenter
'sortable' => true,
'title' => trans('admin/maintenances/form.completion_date'),
'formatter' => 'dateDisplayFormatter',
], [
'field' => 'url',
'searchable' => true,
'sortable' => true,
'title' => trans('general.url'),
'formatter' => 'externalLinkFormatter',
], [
'field' => 'notes',
'searchable' => true,
+5 -1
View File
@@ -14,7 +14,11 @@ class ManufacturerPresenter extends Presenter
public static function dataTableLayout()
{
$layout = [
[
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
],
[
'field' => 'id',
'searchable' => false,
+5
View File
@@ -13,6 +13,11 @@ class SupplierPresenter extends Presenter
public static function dataTableLayout()
{
$layout = [
[
'field' => 'checkbox',
'checkbox' => true,
'titleTooltip' => trans('general.select_all_none'),
],
[
'field' => 'id',
'searchable' => false,
+7
View File
@@ -24,6 +24,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Blade;
/**
* This service provider handles setting the observers on models
@@ -42,6 +43,9 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(UrlGenerator $url)
{
/**
* This is a workaround for proxies/reverse proxies that don't always pass the proper headers.
*
@@ -76,6 +80,9 @@ class AppServiceProvider extends ServiceProvider
Consumable::observe(ConsumableObserver::class);
License::observe(LicenseObserver::class);
Setting::observe(SettingObserver::class);
// https://laravel.com/docs/11.x/blade#html-entity-encoding
Blade::withoutDoubleEncoding();
}
/**
+6 -1
View File
@@ -350,10 +350,15 @@ class BreadcrumbsServiceProvider extends ServiceProvider
* Licenses Breadcrumbs
*/
if ((request()->is('licenses*')) && (request()->status=='inactive')) {
Breadcrumbs::for('licenses.index', fn(Trail $trail) => $trail->parent('home', route('home'))
->push(trans('general.licenses'), route('licenses.index'))
->push(trans('general.show_inactive'), route('licenses.index'))
);
} elseif ((request()->is('licenses*')) && (request()->status=='expiring')) {
Breadcrumbs::for('licenses.index', fn (Trail $trail) =>
$trail->parent('home', route('home'))
->push(trans('general.licenses'), route('licenses.index'))
->push(trans('general.show_inactive'), route('licenses.index'))
->push(trans('general.show_expiring'), route('licenses.index'))
);
} else {
Breadcrumbs::for('licenses.index', fn (Trail $trail) =>
+6 -2
View File
@@ -74,6 +74,10 @@ class Label implements View
[0 => $template->getWidth(), 1 => $template->getHeight(), 'Rotate' => $template->getRotation()]
);
// Required for CJK languages, otherwise the embedded font can get too massive
$pdf->SetFontSubsetting(true);
// Reset parameters
$pdf->SetPrintHeader(false);
$pdf->SetPrintFooter(false);
@@ -176,14 +180,14 @@ class Label implements View
// For fields that have multiple options, we need to combine them
// into a single field so all values are displayed.
->reduce(function ($previous, $current) {
// On the first iteration we simply return the item.
// On the first iteration, we simply return the item.
// If there is only one item to be processed for the row
// then this effectively skips everything below this if block.
if (is_null($previous)) {
return $current;
}
// At this point we are dealing with a row with multiple items being displayed.
// At this point, we are dealing with a row with multiple items being displayed.
// We need to combine the label and value of the current item with the previous item.
// The end result of this will be in this format:
+3 -2
View File
@@ -18,9 +18,10 @@ $dump_options = [
//'add_extra_option' => '--optionname=optionvalue',
];
// Some versions of mysql do not support the --skip-ssl option and will fail if it is even set
// For modern versions of mysqldump, use --ssl-mode=DISABLED
if (env('DB_DUMP_SKIP_SSL') == 'true') {
$dump_options['skip_ssl'] = true;
// Correctly add the option as a string to the 'add_extra_option' key.
$dump_options['add_extra_option'] = '--ssl-mode=DISABLED';
}
+7 -193
View File
@@ -9,11 +9,9 @@
return [
'Global' => [
'Superuser' => [
[
'permission' => 'superuser',
'label' => 'Super User',
'note' => 'Determines whether the user has full access to all aspects of the admin. This setting overrides any more specific permissions throughout the system. ',
'display' => true,
],
],
@@ -21,17 +19,13 @@ return [
'Admin' => [
[
'permission' => 'admin',
'label' => '',
'note' => 'Determines whether the user has access to most aspects of the admin. ',
'display' => true,
],
],
'CSV Import' => [
'Import' => [
[
'permission' => 'import',
'label' => '',
'note' => 'This will allow users to import even if access to users, assets, etc is denied elsewhere.',
'display' => true,
],
],
@@ -39,8 +33,6 @@ return [
'Reports' => [
[
'permission' => 'reports.view',
'label' => 'View',
'note' => 'Determines whether the user has the ability to view reports.',
'display' => true,
],
],
@@ -48,68 +40,48 @@ return [
'Assets' => [
[
'permission' => 'assets.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => false,
],
[
'permission' => 'assets.checkin',
'label' => 'Checkin ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.audit',
'label' => 'Audit ',
'note' => 'Allows the user to mark an asset as physically inventoried.',
'display' => true,
],
[
'permission' => 'assets.view.requestable',
'label' => 'View Requestable Assets',
'note' => '',
'display' => true,
],
[
'permission' => 'assets.view.encrypted_custom_fields',
'label' => 'View and Modify Encrypted Custom Fields',
'note' => '',
'display' => true,
],
@@ -118,44 +90,30 @@ return [
'Accessories' => [
[
'permission' => 'accessories.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.checkin',
'label' => 'Checkin ',
'note' => '',
'display' => true,
],
[
'permission' => 'accessories.files',
'label' => 'View and Modify Accessory Files',
'note' => '',
'display' => true,
],
@@ -164,38 +122,26 @@ return [
'Consumables' => [
[
'permission' => 'consumables.view',
'label' => 'View',
'note' => '',
'display' => true,
],
[
'permission' => 'consumables.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'consumables.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'consumables.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
[
'permission' => 'consumables.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => true,
],
[
'permission' => 'consumables.files',
'label' => 'View and Modify Consumable Files',
'note' => '',
'display' => true,
],
],
@@ -204,44 +150,34 @@ return [
'Licenses' => [
[
'permission' => 'licenses.view',
'label' => 'View',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.checkin',
'display' => true,
],
[
'permission' => 'licenses.keys',
'label' => 'View License Keys',
'note' => '',
'display' => true,
],
[
'permission' => 'licenses.files',
'label' => 'View and Modify License Files',
'note' => '',
'display' => true,
],
],
@@ -250,44 +186,30 @@ return [
'Components' => [
[
'permission' => 'components.view',
'label' => 'View',
'note' => '',
'display' => true,
],
[
'permission' => 'components.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'components.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'components.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
[
'permission' => 'components.checkout',
'label' => 'Checkout ',
'note' => '',
'display' => true,
],
[
'permission' => 'components.checkin',
'label' => 'Checkin ',
'note' => '',
'display' => true,
],
[
'permission' => 'components.files',
'label' => 'View and Modify Component Files',
'note' => '',
'display' => true,
],
@@ -296,26 +218,18 @@ return [
'Kits' => [
[
'permission' => 'kits.view',
'label' => 'View ',
'note' => 'These are predefined kits that can be used to quickly checkout assets, licenses, etc.',
'display' => true,
],
[
'permission' => 'kits.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'kits.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'kits.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -323,26 +237,18 @@ return [
'Users' => [
[
'permission' => 'users.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'users.create',
'label' => 'Create Users',
'note' => '',
'display' => true,
],
[
'permission' => 'users.edit',
'label' => 'Edit Users',
'note' => '',
'display' => true,
],
[
'permission' => 'users.delete',
'label' => 'Delete Users',
'note' => '',
'display' => true,
],
@@ -351,26 +257,18 @@ return [
'Models' => [
[
'permission' => 'models.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'models.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'models.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'models.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
@@ -379,26 +277,18 @@ return [
'Categories' => [
[
'permission' => 'categories.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'categories.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'categories.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'categories.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -406,26 +296,18 @@ return [
'Departments' => [
[
'permission' => 'departments.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'departments.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'departments.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'departments.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -433,26 +315,18 @@ return [
'Status Labels' => [
[
'permission' => 'statuslabels.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'statuslabels.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'statuslabels.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'statuslabels.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -460,26 +334,18 @@ return [
'Custom Fields' => [
[
'permission' => 'customfields.view',
'label' => 'View',
'note' => '',
'display' => true,
],
[
'permission' => 'customfields.create',
'label' => 'Create',
'note' => '',
'display' => true,
],
[
'permission' => 'customfields.edit',
'label' => 'Edit',
'note' => '',
'display' => true,
],
[
'permission' => 'customfields.delete',
'label' => 'Delete',
'note' => '',
'display' => true,
],
],
@@ -487,26 +353,18 @@ return [
'Suppliers' => [
[
'permission' => 'suppliers.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'suppliers.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'suppliers.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'suppliers.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -515,26 +373,18 @@ return [
'Manufacturers' => [
[
'permission' => 'manufacturers.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'manufacturers.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'manufacturers.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'manufacturers.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -542,26 +392,18 @@ return [
'Depreciations' => [
[
'permission' => 'depreciations.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'depreciations.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'depreciations.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'depreciations.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -569,26 +411,18 @@ return [
'Locations' => [
[
'permission' => 'locations.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'locations.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'locations.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'locations.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
@@ -596,66 +430,46 @@ return [
'Companies' => [
[
'permission' => 'companies.view',
'label' => 'View ',
'note' => '',
'display' => true,
],
[
'permission' => 'companies.create',
'label' => 'Create ',
'note' => '',
'display' => true,
],
[
'permission' => 'companies.edit',
'label' => 'Edit ',
'note' => '',
'display' => true,
],
[
'permission' => 'companies.delete',
'label' => 'Delete ',
'note' => '',
'display' => true,
],
],
'Self' => [
'User (Self) Accounts' => [
[
'permission' => 'self.two_factor',
'label' => 'Two-Factor Authentication',
'note' => 'The user may disable/enable two-factor authentication themselves if two-factor is enabled and set to selective.',
'display' => true,
],
[
'permission' => 'self.api',
'label' => 'Create API Keys',
'note' => 'The user create personal API keys to utilize the REST API.',
'display' => true,
],
[
'permission' => 'self.edit_location',
'label' => 'Profile Edit Location',
'note' => 'The user may update their own location in their profile. Note that this is not affected by any additional Users permissions you grant to this user or group.',
'display' => true,
],
[
'permission' => 'self.checkout_assets',
'label' => 'Self-Checkout',
'note' => 'This user may check out assets that are marked for self-checkout.',
'display' => true,
],
[
'permission' => 'self.view_purchase_cost',
'label' => 'View Purchase-Cost Column',
'note' => 'This user can see the purchase cost column of items assigned to them.',
'display' => true,
],
+6 -6
View File
@@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v8.3.2',
'full_app_version' => 'v8.3.2 - build 19905-g028b4e7b7',
'build_version' => '19905',
'app_version' => 'v8.3.4',
'full_app_version' => 'v8.3.4 - build 20218-g3ab2e2011',
'build_version' => '20218',
'prerelease_version' => '',
'hash_version' => 'g028b4e7b7',
'full_hash' => 'v8.3.2-319-g028b4e7b7',
'branch' => 'develop',
'hash_version' => 'g3ab2e2011',
'full_hash' => 'v8.3.4-149-g3ab2e2011',
'branch' => 'master',
);

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